diff --git a/packages/frontend/apps/electron/src/main/recording/feature.ts b/packages/frontend/apps/electron/src/main/recording/feature.ts index 57885fafc9..cd7c46d452 100644 --- a/packages/frontend/apps/electron/src/main/recording/feature.ts +++ b/packages/frontend/apps/electron/src/main/recording/feature.ts @@ -75,6 +75,9 @@ function getNativeModule(): NativeModule { } function cleanup() { + const nativeId = recordingStateMachine.status?.nativeId; + if (nativeId) cleanupAbandonedNativeRecording(nativeId); + recordingStatus$.next(null); shareableContent = null; appStateSubscribers.forEach(subscriber => { try { @@ -186,9 +189,11 @@ function setupNewRunningAppGroup() { }); const debounceStartRecording = debounce((appGroup: AppGroupInfo) => { - // check if the app is running again - if (appGroup.isRunning) { - startRecording(appGroup).catch(err => { + const currentGroup = appGroups$.value.find( + group => group.processGroupId === appGroup.processGroupId + ); + if (currentGroup?.isRunning) { + startRecording(currentGroup).catch(err => { logger.error('failed to start recording', err); }); } @@ -452,7 +457,6 @@ export function setupRecordingFeature() { } export function disableRecordingFeature() { - recordingStatus$.next(null); cleanup(); } @@ -517,7 +521,7 @@ export async function startRecording( return nextState; } catch (error) { if (nativeId) { - await cleanupAbandonedNativeRecording(nativeId); + cleanupAbandonedNativeRecording(nativeId); } logger.error('failed to start recording', error); return recordingStateMachine.dispatch({ @@ -611,11 +615,11 @@ export async function readRecordingFile(filepath: string) { return fsp.readFile(normalizedPath); } -async function cleanupAbandonedNativeRecording(nativeId: string) { +function cleanupAbandonedNativeRecording(nativeId: string) { try { const artifact = getNativeModule().stopRecording(nativeId); const filepath = assertRecordingFilepath(artifact.filepath); - await fsp.rm(filepath, { force: true }); + fs.removeSync(filepath); } catch (error) { logger.error('failed to cleanup abandoned native recording', error); } diff --git a/packages/frontend/native/media_capture/src/recording.rs b/packages/frontend/native/media_capture/src/recording.rs index a9c32e4b40..961ab4f842 100644 --- a/packages/frontend/native/media_capture/src/recording.rs +++ b/packages/frontend/native/media_capture/src/recording.rs @@ -15,6 +15,7 @@ use ogg::writing::{PacketWriteEndInfo, PacketWriter}; use opus_codec::{Application, Channels, Encoder, FrameSize, SampleRate as OpusSampleRate}; use rubato::Resampler; +#[cfg(any(target_os = "macos", target_os = "windows"))] use crate::audio_callback::AudioCallback; #[cfg(target_os = "macos")] use crate::macos::screen_capture_kit::{ApplicationInfo, ShareableContent}; @@ -301,7 +302,7 @@ impl OggOpusWriter { ) .map_err(|e| RecordingError::Encoding(format!("failed to finish stream: {e}")))?; - let _ = self.writer.inner_mut().flush(); + self.writer.inner_mut().flush()?; let size = fs::metadata(&self.filepath)?.len() as i64; let duration_ms = (self.samples_written * 1000) as i64 / self.sample_rate.as_i32() as i64; @@ -551,13 +552,21 @@ pub fn start_recording(opts: RecordingStartOptions) -> Result Result { - let mut recordings = ACTIVE_RECORDINGS - .lock() - .map_err(|_| RecordingError::Start("lock poisoned".into()))?; + let mut entry = { + let mut recordings = ACTIVE_RECORDINGS + .lock() + .map_err(|_| RecordingError::Start("lock poisoned".into()))?; - let mut entry = recordings.remove(&id).ok_or(RecordingError::NotFound)?; + recordings.remove(&id).ok_or(RecordingError::NotFound)? + }; - entry.capture.stop().map_err(|e| RecordingError::Start(e.to_string()))?; + if let Err(error) = entry.capture.stop() { + ACTIVE_RECORDINGS + .lock() + .map_err(|_| RecordingError::Start("lock poisoned".into()))? + .insert(id, entry); + return Err(RecordingError::Start(error.to_string()).into()); + } drop(entry.sender.take());