@@ -557,10 +557,7 @@ fn camera_id_label(id: &camera::DeviceOrModelID) -> String {
557557 }
558558}
559559
560- fn camera_lock_matches_id (
561- lock : & CameraFeedLock ,
562- selected_id : & camera:: DeviceOrModelID ,
563- ) -> bool {
560+ fn camera_lock_matches_id ( lock : & CameraFeedLock , selected_id : & camera:: DeviceOrModelID ) -> bool {
564561 let camera_info = lock. camera_info ( ) ;
565562 match selected_id {
566563 camera:: DeviceOrModelID :: DeviceID ( device_id) => camera_info. device_id ( ) == device_id,
@@ -582,7 +579,7 @@ async fn initialize_selected_camera(
582579 . await
583580 . map_err ( |err| anyhow ! ( "Failed to initialize selected camera '{label}': {err}" ) ) ?;
584581
585- ready. await . map_err ( |err| match err {
582+ ready. await . map ( |_| ( ) ) . map_err ( |err| match err {
586583 camera:: SetInputError :: DeviceNotFound => {
587584 anyhow ! ( "Selected camera '{label}' is no longer available" )
588585 }
@@ -597,9 +594,7 @@ async fn lock_initialized_camera(
597594 let label = camera_id_label ( id) ;
598595 match camera_feed. ask ( camera:: Lock ) . await {
599596 Ok ( lock) => Ok ( lock) ,
600- Err ( kameo:: error:: SendError :: HandlerError (
601- camera:: LockFeedError :: NoInput ,
602- ) ) => Err ( anyhow ! (
597+ Err ( kameo:: error:: SendError :: HandlerError ( camera:: LockFeedError :: NoInput ) ) => Err ( anyhow ! (
603598 "Selected camera '{label}' did not become ready after initialization"
604599 ) ) ,
605600 Err ( err) => Err ( anyhow ! ( "Failed to lock selected camera '{label}': {err}" ) ) ,
@@ -655,9 +650,7 @@ async fn lock_selected_camera(
655650 tokio:: time:: sleep ( Duration :: from_millis ( 50 ) ) . await ;
656651 None
657652 }
658- Err ( kameo:: error:: SendError :: HandlerError (
659- camera:: LockFeedError :: NoInput ,
660- ) ) => None ,
653+ Err ( kameo:: error:: SendError :: HandlerError ( camera:: LockFeedError :: NoInput ) ) => None ,
661654 Err ( err) => {
662655 return Err ( anyhow ! (
663656 "Failed to lock selected camera '{}': {err}" ,
@@ -690,7 +683,7 @@ async fn initialize_selected_microphone(
690683 . await
691684 . map_err ( |err| anyhow ! ( "Failed to initialize selected microphone '{label}': {err}" ) ) ?;
692685
693- ready. await . map_err ( |err| match err {
686+ ready. await . map ( |_| ( ) ) . map_err ( |err| match err {
694687 microphone:: SetInputError :: DeviceNotFound => {
695688 anyhow ! ( "Selected microphone '{label}' is no longer available" )
696689 }
@@ -704,12 +697,12 @@ async fn lock_initialized_microphone(
704697) -> anyhow:: Result < microphone:: MicrophoneFeedLock > {
705698 match mic_feed. ask ( microphone:: Lock ) . await {
706699 Ok ( lock) => Ok ( lock) ,
707- Err ( kameo:: error:: SendError :: HandlerError (
708- microphone:: LockFeedError :: NoInput ,
709- ) ) => Err ( anyhow ! (
710- "Selected microphone '{label}' did not become ready after initialization"
700+ Err ( kameo:: error:: SendError :: HandlerError ( microphone:: LockFeedError :: NoInput ) ) => Err (
701+ anyhow ! ( "Selected microphone '{label}' did not become ready after initialization" ) ,
702+ ) ,
703+ Err ( err) => Err ( anyhow ! (
704+ "Failed to lock selected microphone '{label}': {err}"
711705 ) ) ,
712- Err ( err) => Err ( anyhow ! ( "Failed to lock selected microphone '{label}': {err}" ) ) ,
713706 }
714707}
715708
@@ -1102,9 +1095,7 @@ pub async fn start_recording(
11021095 let inputs = inputs. clone ( ) ;
11031096 async move {
11041097 fail ! ( "recording::spawn_actor" ) ;
1105- use kameo:: error:: SendError ;
11061098
1107- // Initialize camera if selected but not active
11081099 let ( camera_feed_actor, selected_camera_id, selected_camera_settings) = {
11091100 let state = state_mtx. read ( ) . await ;
11101101 let selected_camera_settings = state. selected_camera_id . as_ref ( ) . and_then ( |id| {
@@ -1120,52 +1111,16 @@ pub async fn start_recording(
11201111 )
11211112 } ;
11221113
1123- let camera_lock_result = camera_feed_actor. ask ( camera:: Lock ) . await ;
1124-
1125- let camera_feed_lock = match camera_lock_result {
1126- Ok ( lock) => Some ( lock) ,
1127- Err ( SendError :: HandlerError ( camera:: LockFeedError :: NoInput ) ) => {
1128- if let Some ( id) = selected_camera_id {
1129- info ! (
1130- "Camera selected but not initialized, initializing: {:?}" ,
1131- id
1132- ) ;
1133- match camera_feed_actor
1134- . ask ( camera:: SetInput {
1135- id : id. clone ( ) ,
1136- settings : selected_camera_settings,
1137- } )
1138- . await
1139- {
1140- Ok ( fut) => match fut. await {
1141- Ok ( _) => match camera_feed_actor. ask ( camera:: Lock ) . await {
1142- Ok ( lock) => Some ( lock) ,
1143- Err ( e) => {
1144- warn ! ( "Failed to lock camera after initialization: {}" , e) ;
1145- None
1146- }
1147- } ,
1148- Err ( e) => {
1149- warn ! ( "Failed to initialize camera: {}" , e) ;
1150- None
1151- }
1152- } ,
1153- Err ( e) => {
1154- warn ! ( "Failed to ask SetInput: {}" , e) ;
1155- None
1156- }
1157- }
1158- } else {
1159- None
1160- }
1161- }
1162- Err ( e) => return Err ( anyhow ! ( e. to_string( ) ) ) ,
1163- } ;
1114+ let camera_feed = lock_selected_camera (
1115+ & camera_feed_actor,
1116+ selected_camera_id,
1117+ selected_camera_settings,
1118+ & inputs. capture_target ,
1119+ )
1120+ . await ?;
11641121
11651122 let mut state = state_mtx. write ( ) . await ;
11661123
1167- let camera_feed = camera_feed_lock. map ( Arc :: new) ;
1168-
11691124 state. camera_in_use = camera_feed. is_some ( ) ;
11701125
11711126 #[ cfg( target_os = "macos" ) ]
@@ -1801,34 +1756,30 @@ async fn lock_selected_microphone(
18011756 return Ok ( None ) ;
18021757 } ;
18031758
1804- match mic_feed. ask ( microphone:: Lock ) . await {
1805- Ok ( lock) => return Ok ( Some ( Arc :: new ( lock) ) ) ,
1806- Err ( kameo:: error:: SendError :: HandlerError ( microphone:: LockFeedError :: NoInput ) ) => { }
1807- Err ( err) => return Err ( anyhow ! ( err. to_string( ) ) ) ,
1808- }
1809-
1810- let ready = mic_feed
1811- . ask ( microphone:: SetInput {
1812- label : label. clone ( ) ,
1813- settings : selected_settings,
1814- } )
1815- . await
1816- . map_err ( |err| anyhow ! ( err. to_string( ) ) ) ?;
1817-
1818- ready. await . map_err ( |err| match err {
1819- microphone:: SetInputError :: DeviceNotFound => {
1820- anyhow ! ( "Selected microphone '{label}' is no longer available" )
1759+ let existing_lock = match mic_feed. ask ( microphone:: Lock ) . await {
1760+ Ok ( lock) if lock. device_name ( ) == label => Some ( lock) ,
1761+ Ok ( lock) => {
1762+ drop ( lock) ;
1763+ tokio:: time:: sleep ( Duration :: from_millis ( 50 ) ) . await ;
1764+ None
18211765 }
1822- err => anyhow ! ( "Failed to initialize selected microphone '{label}': {err}" ) ,
1823- } ) ?;
1766+ Err ( kameo:: error:: SendError :: HandlerError ( microphone:: LockFeedError :: NoInput ) ) => None ,
1767+ Err ( err) => {
1768+ return Err ( anyhow ! (
1769+ "Failed to lock selected microphone '{label}': {err}"
1770+ ) ) ;
1771+ }
1772+ } ;
18241773
1825- match mic_feed. ask ( microphone:: Lock ) . await {
1826- Ok ( lock) => Ok ( Some ( Arc :: new ( lock) ) ) ,
1827- Err ( kameo:: error:: SendError :: HandlerError ( microphone:: LockFeedError :: NoInput ) ) => Err (
1828- anyhow ! ( "Selected microphone '{label}' did not become ready after initialization" ) ,
1829- ) ,
1830- Err ( err) => Err ( anyhow ! ( err. to_string( ) ) ) ,
1831- }
1774+ let lock = if let Some ( lock) = existing_lock {
1775+ lock
1776+ } else {
1777+ initialize_selected_microphone ( mic_feed, & label, selected_settings) . await ?;
1778+ lock_initialized_microphone ( mic_feed, & label) . await ?
1779+ } ;
1780+
1781+ validate_microphone_receiving ( & lock, & label) . await ?;
1782+ Ok ( Some ( Arc :: new ( lock) ) )
18321783}
18331784
18341785fn mic_actor_not_running ( err : & anyhow:: Error ) -> bool {
@@ -1866,6 +1817,92 @@ where
18661817 }
18671818}
18681819
1820+ async fn cancel_discarded_recording (
1821+ app : & AppHandle ,
1822+ recording : InProgressRecording ,
1823+ ) -> Option < String > {
1824+ match recording {
1825+ InProgressRecording :: Instant {
1826+ handle,
1827+ segment_upload,
1828+ video_upload_info,
1829+ ..
1830+ } => {
1831+ let video_id = video_upload_info. id ;
1832+ segment_upload. handle . abort ( ) ;
1833+
1834+ if let Err ( err) = handle. cancel ( ) . await {
1835+ warn ! ( "Failed to cancel instant recording while discarding: {err:#}" ) ;
1836+ }
1837+
1838+ match segment_upload. handle . await {
1839+ Ok ( Ok ( ( ) ) ) => { }
1840+ Ok ( Err ( err) ) => warn ! ( "Instant upload ended while discarding recording: {err}" ) ,
1841+ Err ( err) if err. is_cancelled ( ) => { }
1842+ Err ( err) => {
1843+ warn ! ( "Failed to join instant upload while discarding recording: {err}" )
1844+ }
1845+ }
1846+
1847+ crate :: upload:: emit_upload_complete ( app, & video_id) ;
1848+ Some ( video_id)
1849+ }
1850+ InProgressRecording :: Studio { handle, .. } => {
1851+ if let Err ( err) = handle. cancel ( ) . await {
1852+ warn ! ( "Failed to cancel studio recording while discarding: {err:#}" ) ;
1853+ }
1854+
1855+ None
1856+ }
1857+ }
1858+ }
1859+
1860+ async fn remove_recording_dir ( recording_dir : & Path ) -> Result < ( ) , String > {
1861+ match tokio:: fs:: remove_dir_all ( recording_dir) . await {
1862+ Ok ( ( ) ) => Ok ( ( ) ) ,
1863+ Err ( err) if err. kind ( ) == std:: io:: ErrorKind :: NotFound => Ok ( ( ) ) ,
1864+ Err ( err) => Err ( format ! ( "Failed to delete recording files: {err}" ) ) ,
1865+ }
1866+ }
1867+
1868+ async fn delete_remote_instant_video ( app : & AppHandle , video_id : & str ) -> Result < ( ) , String > {
1869+ let response = app
1870+ . authed_api_request (
1871+ format ! ( "/api/desktop/video/delete?videoId={video_id}" ) ,
1872+ |client, url| client. delete ( url) ,
1873+ )
1874+ . await
1875+ . map_err ( |err| format ! ( "Failed to delete instant recording: {err}" ) ) ?;
1876+
1877+ let status = response. status ( ) ;
1878+ if status. is_success ( ) || status == reqwest:: StatusCode :: NOT_FOUND {
1879+ return Ok ( ( ) ) ;
1880+ }
1881+
1882+ let body = response
1883+ . text ( )
1884+ . await
1885+ . unwrap_or_else ( |err| format ! ( "Failed to read response body: {err}" ) ) ;
1886+
1887+ Err ( format ! (
1888+ "Failed to delete instant recording {video_id}: {status}: {body}"
1889+ ) )
1890+ }
1891+
1892+ async fn discard_recording ( app : & AppHandle , recording : InProgressRecording ) -> Result < ( ) , String > {
1893+ let recording_dir = recording. recording_dir ( ) . clone ( ) ;
1894+ let video_id = cancel_discarded_recording ( app, recording) . await ;
1895+ let local_delete = remove_recording_dir ( & recording_dir) . await ;
1896+ let remote_delete = if let Some ( video_id) = video_id {
1897+ delete_remote_instant_video ( app, & video_id) . await
1898+ } else {
1899+ Ok ( ( ) )
1900+ } ;
1901+
1902+ remote_delete?;
1903+ local_delete
1904+ }
1905+
18691906#[ tauri:: command]
18701907#[ specta:: specta]
18711908#[ instrument( skip( app, state) ) ]
@@ -1910,7 +1947,7 @@ pub async fn restart_recording(
19101947
19111948 let inputs = recording. inputs ( ) . clone ( ) ;
19121949
1913- let _ = recording. cancel ( ) . await ;
1950+ discard_recording ( & app , recording) . await ? ;
19141951
19151952 tokio:: time:: sleep ( Duration :: from_millis ( 1000 ) ) . await ;
19161953
@@ -1927,41 +1964,11 @@ pub async fn delete_recording(app: AppHandle, state: MutableState<'_, App>) -> R
19271964 } ;
19281965
19291966 if let Some ( recording) = recording_data {
1930- let recording_dir = recording. recording_dir ( ) . clone ( ) ;
19311967 CurrentRecordingChanged . emit ( & app) . ok ( ) ;
19321968 RecordingStopped { } . emit ( & app) . ok ( ) ;
19331969
1934- let video_id = match & recording {
1935- InProgressRecording :: Instant {
1936- video_upload_info,
1937- segment_upload,
1938- ..
1939- } => {
1940- debug ! (
1941- "User deleted recording. Aborting multipart upload for {:?}" ,
1942- video_upload_info. id
1943- ) ;
1944- segment_upload. handle . abort ( ) ;
1945-
1946- Some ( video_upload_info. id . clone ( ) )
1947- }
1948- _ => None ,
1949- } ;
1950-
1951- let _ = recording. cancel ( ) . await ;
1970+ let delete_result = discard_recording ( & app, recording) . await ;
19521971
1953- std:: fs:: remove_dir_all ( & recording_dir) . ok ( ) ;
1954-
1955- if let Some ( id) = video_id {
1956- let _ = app
1957- . authed_api_request (
1958- format ! ( "/api/desktop/video/delete?videoId={id}" ) ,
1959- |c, url| c. delete ( url) ,
1960- )
1961- . await ;
1962- }
1963-
1964- // Check user's post-deletion behavior setting
19651972 let settings = GeneralSettingsStore :: get ( & app)
19661973 . ok ( )
19671974 . flatten ( )
@@ -1981,6 +1988,8 @@ pub async fn delete_recording(app: AppHandle, state: MutableState<'_, App>) -> R
19811988 . await ;
19821989 }
19831990 }
1991+
1992+ delete_result?;
19841993 }
19851994
19861995 Ok ( ( ) )
0 commit comments