Skip to content

Commit

Permalink
fix: Copy file after exporting retries
Browse files Browse the repository at this point in the history
  • Loading branch information
richiemcilroy committed Dec 9, 2024
1 parent 28d8fc6 commit 1473e0b
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 28 deletions.
136 changes: 108 additions & 28 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use cap_project::{Content, ProjectConfiguration, RecordingMeta, SharingMeta};
use cap_recording::RecordingOptions;
use cap_rendering::ProjectRecordings;
// use display::{list_capture_windows, Bounds, CaptureTarget, FPS};
use cap_export::is_valid_mp4;
use general_settings::GeneralSettingsStore;
use mp4::Mp4Reader;
use notifications::NotificationType;
Expand Down Expand Up @@ -633,39 +634,118 @@ async fn get_rendered_video_path(app: AppHandle, video_id: String) -> Result<Pat
async fn copy_file_to_path(app: AppHandle, src: String, dst: String) -> Result<(), String> {
println!("Attempting to copy file from {} to {}", src, dst);

// Determine if this is a screenshot based on the path
let is_screenshot = src.contains("screenshots/");

match tokio::fs::copy(&src, &dst).await {
Ok(bytes) => {
println!(
"Successfully copied {} bytes from {} to {}",
bytes, src, dst
);
// Send appropriate success notification
notifications::send_notification(
&app,
if is_screenshot {
notifications::NotificationType::ScreenshotSaved
} else {
notifications::NotificationType::VideoSaved
},
);
Ok(())
let src_path = std::path::Path::new(&src);
if !src_path.exists() {
return Err(format!("Source file {} does not exist", src));
}

if !is_screenshot {
if !is_valid_mp4(src_path) {
// Wait for up to 10 seconds for the file to become valid
let mut attempts = 0;
while attempts < 10 {
std::thread::sleep(std::time::Duration::from_secs(1));
if is_valid_mp4(src_path) {
break;
}
attempts += 1;
}
if attempts == 10 {
return Err("Source video file is not a valid MP4".to_string());
}
}
Err(e) => {
eprintln!("Failed to copy file from {} to {}: {}", src, dst, e);
notifications::send_notification(
&app,
if is_screenshot {
notifications::NotificationType::ScreenshotSaveFailed
} else {
notifications::NotificationType::VideoSaveFailed
},
);
Err(e.to_string())
}

if let Some(parent) = std::path::Path::new(&dst).parent() {
tokio::fs::create_dir_all(parent)
.await
.map_err(|e| format!("Failed to create target directory: {}", e))?;
}

let mut attempts = 0;
const MAX_ATTEMPTS: u32 = 3;
let mut last_error = None;

while attempts < MAX_ATTEMPTS {
match tokio::fs::copy(&src, &dst).await {
Ok(bytes) => {
let src_size = match tokio::fs::metadata(&src).await {
Ok(metadata) => metadata.len(),
Err(e) => {
last_error = Some(format!("Failed to get source file metadata: {}", e));
attempts += 1;
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
continue;
}
};

if bytes != src_size {
last_error = Some(format!(
"File copy verification failed: copied {} bytes but source is {} bytes",
bytes, src_size
));
let _ = tokio::fs::remove_file(&dst).await;
attempts += 1;
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
continue;
}

if !is_screenshot && !is_valid_mp4(std::path::Path::new(&dst)) {
last_error = Some("Destination file is not a valid MP4".to_string());
// Delete the invalid file
let _ = tokio::fs::remove_file(&dst).await;
attempts += 1;
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
continue;
}

println!(
"Successfully copied {} bytes from {} to {}",
bytes, src, dst
);

notifications::send_notification(
&app,
if is_screenshot {
notifications::NotificationType::ScreenshotSaved
} else {
notifications::NotificationType::VideoSaved
},
);
return Ok(());
}
Err(e) => {
last_error = Some(e.to_string());
attempts += 1;
if attempts < MAX_ATTEMPTS {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
continue;
}
}
}
}

// If we get here, all attempts failed
eprintln!(
"Failed to copy file from {} to {} after {} attempts. Last error: {}",
src,
dst,
MAX_ATTEMPTS,
last_error.as_ref().unwrap()
);

notifications::send_notification(
&app,
if is_screenshot {
notifications::NotificationType::ScreenshotSaveFailed
} else {
notifications::NotificationType::VideoSaveFailed
},
);

Err(last_error.unwrap_or_else(|| "Maximum retry attempts exceeded".to_string()))
}

#[tauri::command]
Expand Down
15 changes: 15 additions & 0 deletions crates/export/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use cap_editor::Segment;
use image::{ImageBuffer, Rgba};
use mp4::Mp4Reader;
use std::path::Path;
use std::{path::PathBuf, sync::Arc};

use cap_media::feeds::AudioFrameBuffer;
Expand Down Expand Up @@ -308,3 +309,17 @@ pub async fn export_video_to_file(

Ok(output_path)
}

/// Validates if a file at the given path is a valid MP4 file
pub fn is_valid_mp4(path: &Path) -> bool {
if let Ok(file) = std::fs::File::open(path) {
let file_size = match file.metadata() {
Ok(metadata) => metadata.len(),
Err(_) => return false,
};
let reader = std::io::BufReader::new(file);
Mp4Reader::read_header(reader, file_size).is_ok()
} else {
false
}
}

1 comment on commit 1473e0b

@vercel
Copy link

@vercel vercel bot commented on 1473e0b Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.