Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mod presets;
mod recording;
mod recording_settings;
mod recovery;
mod speed_test;
mod screenshot_editor;
mod target_select_overlay;
mod thumbnails;
Expand Down Expand Up @@ -3141,6 +3142,9 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
recovery::find_incomplete_recordings,
recovery::recover_recording,
recovery::discard_incomplete_recording,
speed_test::run_speed_test,
speed_test::run_health_check,
speed_test::get_network_status,
])
.events(tauri_specta::collect_events![
RecordingOptionsChanged,
Expand All @@ -3167,6 +3171,8 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
import::VideoImportProgress,
SetCaptureAreaPending,
DevicesUpdated,
speed_test::SpeedTestUpdate,
speed_test::HealthCheckUpdate,
])
.error_handling(tauri_specta::ErrorHandlingMode::Throw)
.typ::<ProjectConfiguration>()
Expand Down Expand Up @@ -3310,6 +3316,7 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
app.manage(panel_manager::PanelManager::new());
app.manage(http_client::HttpClient::default());
app.manage(http_client::RetryableHttpClient::default());
app.manage(Arc::new(RwLock::new(speed_test::NetworkState::default())));
app.manage(PendingScreenshots::default());
app.manage(FinalizingRecordings::default());

Expand Down Expand Up @@ -3456,6 +3463,8 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {

audio_meter::spawn_event_emitter(app.clone(), mic_samples_rx);

speed_test::spawn_startup_health_check(app.clone());

if let Err(err) = tray::create_tray(&app) {
error!("Failed to create tray: {err}");
}
Expand Down
33 changes: 27 additions & 6 deletions apps/desktop/src-tauri/src/recording.rs
Original file line number Diff line number Diff line change
Expand Up @@ -923,17 +923,34 @@ pub async fn start_recording(
return Err(anyhow!("Video upload info not found"));
};

let settings_resolution = general_settings
.as_ref()
.map(|settings| settings.instant_mode_max_resolution)
.unwrap_or(1920);

let speed_test_resolution = {
let network_state = app_handle
.state::<std::sync::Arc<tokio::sync::RwLock<crate::speed_test::NetworkState>>>();
let state = network_state.read().await;
match &state.speed_test_status {
crate::speed_test::SpeedTestStatus::Completed(result) => {
Some(result.recommended_quality.max_resolution())
}
_ => None,
}
};

let max_resolution = match speed_test_resolution {
Some(speed_res) => settings_resolution.min(speed_res),
None => settings_resolution,
};

let mut builder = instant_recording::Actor::builder(
recording_dir.clone(),
inputs.capture_target.clone(),
)
.with_system_audio(inputs.capture_system_audio)
.with_max_output_size(
general_settings
.as_ref()
.map(|settings| settings.instant_mode_max_resolution)
.unwrap_or(1920),
);
.with_max_output_size(max_resolution);

#[cfg(target_os = "macos")]
{
Expand Down Expand Up @@ -1044,6 +1061,8 @@ pub async fn start_recording(
let _ = RecordingEvent::Started.emit(&app);
let _ = RecordingStarted.emit(&app);

crate::speed_test::set_recording_active(&app, true).await;
Copy link
Contributor

Choose a reason for hiding this comment

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

Recording failure leaves speed test permanently blocked

When a recording fails or crashes (the error path in spawn_actor at line ~1078), set_recording_active(&app, false) is never called. The flag is set to true at line 1064, but only stop_recording (line 1257) resets it to false. If the recording actor errors out via the Err(e) branch, is_recording stays true forever, permanently blocking the speed test with "Cannot run speed test during an active recording".

You should reset the flag in the error handler as well:

                Err(e) => {
                    let mut state = state_mtx.write().await;

                    crate::speed_test::set_recording_active(&app, false).await;

                    let _ = RecordingEvent::Failed {
                        error: e.to_string(),
                    }
                    .emit(&app);
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/recording.rs
Line: 1064

Comment:
**Recording failure leaves speed test permanently blocked**

When a recording fails or crashes (the error path in `spawn_actor` at line ~1078), `set_recording_active(&app, false)` is never called. The flag is set to `true` at line 1064, but only `stop_recording` (line 1257) resets it to `false`. If the recording actor errors out via the `Err(e)` branch, `is_recording` stays `true` forever, permanently blocking the speed test with "Cannot run speed test during an active recording".

You should reset the flag in the error handler as well:

```rust
                Err(e) => {
                    let mut state = state_mtx.write().await;

                    crate::speed_test::set_recording_active(&app, false).await;

                    let _ = RecordingEvent::Failed {
                        error: e.to_string(),
                    }
                    .emit(&app);
```

How can I resolve this? If you propose a fix, please make it concise.


spawn_actor({
let app = app.clone();
let state_mtx = Arc::clone(&state_mtx);
Expand Down Expand Up @@ -1235,6 +1254,8 @@ fn mic_actor_not_running(err: &anyhow::Error) -> bool {
#[specta::specta]
#[instrument(skip(app, state))]
pub async fn stop_recording(app: AppHandle, state: MutableState<'_, App>) -> Result<(), String> {
crate::speed_test::set_recording_active(&app, false).await;
Copy link

Choose a reason for hiding this comment

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

Minor logic bug: setting is_recording = false at the top of stop_recording opens a window where run_speed_test can run while current_recording.stop().await is still in progress. I’d move the flag flip to after stop() completes (or right before returning) so the speed-test guard matches reality.


let mut state = state.write().await;
let Some(current_recording) = state.clear_current_recording() else {
return Err("Recording not in progress".to_string())?;
Expand Down
Loading