Skip to content
Merged
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
55 changes: 45 additions & 10 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use general_settings::GeneralSettingsStore;
use mp4::Mp4Reader;
use notifications::NotificationType;
use png::{ColorType, Encoder};
use recording::InProgressRecording;
use recording::{InProgressRecording, StartRecordingInputs};
use relative_path::RelativePathBuf;

use scap::capturer::Capturer;
Expand Down Expand Up @@ -85,6 +85,12 @@ use windows::EditorWindowIds;
use windows::set_window_transparent;
use windows::{CapWindowId, ShowCapWindow};

pub enum RecordingState {
None,
Pending,
Active(InProgressRecording),
}

#[derive(specta::Type, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct App {
Expand All @@ -104,7 +110,7 @@ pub struct App {
#[serde(skip)]
handle: AppHandle,
#[serde(skip)]
current_recording: Option<InProgressRecording>,
recording_state: RecordingState,
#[serde(skip)]
recording_logging_handle: LoggingHandle,
server_url: String,
Expand Down Expand Up @@ -140,16 +146,27 @@ pub struct VideoUploadInfo {
}

impl App {
pub fn set_current_recording(&mut self, actor: InProgressRecording) {
self.current_recording = Some(actor);
pub fn set_pending_recording(&mut self) {
self.recording_state = RecordingState::Pending;
CurrentRecordingChanged.emit(&self.handle).ok();
}

pub fn set_current_recording(&mut self, actor: InProgressRecording) {
self.recording_state = RecordingState::Active(actor);
CurrentRecordingChanged.emit(&self.handle).ok();
}

pub fn clear_current_recording(&mut self) -> Option<InProgressRecording> {
self.close_occluder_windows();

self.current_recording.take()
match std::mem::replace(&mut self.recording_state, RecordingState::None) {
RecordingState::Active(recording) => {
self.close_occluder_windows();
Some(recording)
}
_ => {
self.close_occluder_windows();
None
}
}
}

fn close_occluder_windows(&self) {
Expand All @@ -175,6 +192,24 @@ impl App {

Ok(())
}

pub fn current_recording(&self) -> Option<&InProgressRecording> {
match &self.recording_state {
RecordingState::Active(recording) => Some(recording),
_ => None,
}
}

pub fn current_recording_mut(&mut self) -> Option<&mut InProgressRecording> {
match &mut self.recording_state {
RecordingState::Active(recording) => Some(recording),
_ => None,
}
}

pub fn is_recording_active_or_pending(&self) -> bool {
!matches!(self.recording_state, RecordingState::None)
}
}

#[tauri::command]
Expand Down Expand Up @@ -389,7 +424,7 @@ async fn get_current_recording(
state: MutableState<'_, App>,
) -> Result<JsonValue<Option<CurrentRecording>>, ()> {
let state = state.read().await;
Ok(JsonValue::new(&state.current_recording.as_ref().map(|r| {
Ok(JsonValue::new(&state.current_recording().map(|r| {
let bounds = r.bounds();

let target = match r.capture_target() {
Expand Down Expand Up @@ -2041,7 +2076,7 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
camera_feed_initialization: None,
mic_samples_tx: audio_input_tx,
mic_feed: None,
current_recording: None,
recording_state: RecordingState::None,
recording_logging_handle,
server_url: GeneralSettingsStore::get(&app)
.ok()
Expand Down Expand Up @@ -2130,7 +2165,7 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
let state = app.state::<Arc<RwLock<App>>>();
let app_state = &mut *state.write().await;

if app_state.current_recording.is_none() {
if !app_state.is_recording_active_or_pending() {
app_state.mic_feed.take();
app_state.camera_feed.take();

Expand Down
14 changes: 10 additions & 4 deletions apps/desktop/src-tauri/src/recording.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ pub fn list_cameras() -> Vec<cap_camera::CameraInfo> {
CameraFeed::list_cameras()
}

#[derive(Deserialize, Type, Clone)]
#[derive(Deserialize, Type, Clone, Debug)]
pub struct StartRecordingInputs {
pub capture_target: ScreenCaptureTarget,
#[serde(default)]
Expand Down Expand Up @@ -327,6 +327,12 @@ pub async fn start_recording(
_ => {}
}

// Set pending state BEFORE closing main window and starting countdown
{
let mut state = state_mtx.write().await;
state.set_pending_recording();
}

if let Some(window) = CapWindowId::Main.get(&app) {
let _ = general_settings
.map(|v| v.main_window_recording_start_behaviour)
Expand Down Expand Up @@ -496,7 +502,7 @@ pub async fn start_recording(
pub async fn pause_recording(state: MutableState<'_, App>) -> Result<(), String> {
let mut state = state.write().await;

if let Some(recording) = state.current_recording.as_mut() {
if let Some(recording) = state.current_recording_mut() {
recording.pause().await.map_err(|e| e.to_string())?;
}

Expand All @@ -508,7 +514,7 @@ pub async fn pause_recording(state: MutableState<'_, App>) -> Result<(), String>
pub async fn resume_recording(state: MutableState<'_, App>) -> Result<(), String> {
let mut state = state.write().await;

if let Some(recording) = state.current_recording.as_mut() {
if let Some(recording) = state.current_recording_mut() {
recording.resume().await.map_err(|e| e.to_string())?;
}

Expand Down Expand Up @@ -595,7 +601,7 @@ async fn handle_recording_end(
app: &mut App,
) -> Result<(), String> {
// Clear current recording, just in case :)
app.current_recording.take();
app.clear_current_recording();

let res = if let Some(recording) = recording {
// we delay reporting errors here so that everything else happens first
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/utils/tauri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,8 @@ export type PresetsStore = { presets: Preset[]; default: number | null }
export type ProjectConfiguration = { aspectRatio: AspectRatio | null; background: BackgroundConfiguration; camera: Camera; audio: AudioConfiguration; cursor: CursorConfiguration; hotkeys: HotkeysConfiguration; timeline?: TimelineConfiguration | null; captions?: CaptionsData | null }
export type ProjectRecordingsMeta = { segments: SegmentRecordings[] }
export type RecordingEvent = { variant: "Countdown"; value: number } | { variant: "Started" } | { variant: "Stopped" } | { variant: "Failed"; error: string }
export type RecordingMeta = (StudioRecordingMeta | InstantRecordingMeta) & { platform: Platform | null; pretty_name: string; sharing?: SharingMeta | null }
export type RecordingMetaWithType = ((StudioRecordingMeta | InstantRecordingMeta) & { platform: Platform | null; pretty_name: string; sharing?: SharingMeta | null }) & { type: RecordingType }
export type RecordingMeta = (StudioRecordingMeta | InstantRecordingMeta) & { platform?: Platform | null; pretty_name: string; sharing?: SharingMeta | null }
export type RecordingMetaWithType = ((StudioRecordingMeta | InstantRecordingMeta) & { platform?: Platform | null; pretty_name: string; sharing?: SharingMeta | null }) & { type: RecordingType }
export type RecordingMode = "studio" | "instant"
export type RecordingOptionsChanged = null
export type RecordingStarted = null
Expand Down
Loading