@@ -3,7 +3,7 @@ use cap_recording::{
33} ;
44use serde:: { Deserialize , Serialize } ;
55use std:: path:: { Path , PathBuf } ;
6- use tauri:: { AppHandle , Manager , Url } ;
6+ use tauri:: { AppHandle , Emitter , Manager , Url } ;
77use tracing:: trace;
88
99use crate :: { App , ArcLock , recording:: StartRecordingInputs , windows:: ShowCapWindow } ;
@@ -25,7 +25,19 @@ pub enum DeepLinkAction {
2525 capture_system_audio : bool ,
2626 mode : RecordingMode ,
2727 } ,
28+ PauseRecording ,
29+ ResumeRecording ,
30+ TogglePauseRecording ,
2831 StopRecording ,
32+ TakeScreenshot ,
33+ SetCamera {
34+ id : String ,
35+ } ,
36+ SetMicrophone {
37+ label : String ,
38+ } ,
39+ ListCameras ,
40+ ListMicrophones ,
2941 OpenEditor {
3042 project_path : PathBuf ,
3143 } ,
@@ -34,42 +46,6 @@ pub enum DeepLinkAction {
3446 } ,
3547}
3648
37- pub fn handle ( app_handle : & AppHandle , urls : Vec < Url > ) {
38- trace ! ( "Handling deep actions for: {:?}" , & urls) ;
39-
40- let actions: Vec < _ > = urls
41- . into_iter ( )
42- . filter ( |url| !url. as_str ( ) . is_empty ( ) )
43- . filter_map ( |url| {
44- DeepLinkAction :: try_from ( & url)
45- . map_err ( |e| match e {
46- ActionParseFromUrlError :: ParseFailed ( msg) => {
47- eprintln ! ( "Failed to parse deep link \" {}\" : {}" , & url, msg)
48- }
49- ActionParseFromUrlError :: Invalid => {
50- eprintln ! ( "Invalid deep link format \" {}\" " , & url)
51- }
52- // Likely login action, not handled here.
53- ActionParseFromUrlError :: NotAction => { }
54- } )
55- . ok ( )
56- } )
57- . collect ( ) ;
58-
59- if actions. is_empty ( ) {
60- return ;
61- }
62-
63- let app_handle = app_handle. clone ( ) ;
64- tauri:: async_runtime:: spawn ( async move {
65- for action in actions {
66- if let Err ( e) = action. execute ( & app_handle) . await {
67- eprintln ! ( "Failed to handle deep link action: {e}" ) ;
68- }
69- }
70- } ) ;
71- }
72-
7349pub enum ActionParseFromUrlError {
7450 ParseFailed ( String ) ,
7551 Invalid ,
@@ -80,35 +56,79 @@ impl TryFrom<&Url> for DeepLinkAction {
8056 type Error = ActionParseFromUrlError ;
8157
8258 fn try_from ( url : & Url ) -> Result < Self , Self :: Error > {
83- #[ cfg( target_os = "macos" ) ]
84- if url. scheme ( ) == "file" {
85- return url
86- . to_file_path ( )
87- . map ( |project_path| Self :: OpenEditor { project_path } )
88- . map_err ( |_| ActionParseFromUrlError :: Invalid ) ;
59+ if url. scheme ( ) != "cap-desktop" {
60+ return Err ( ActionParseFromUrlError :: NotAction ) ;
8961 }
9062
9163 match url. domain ( ) {
92- Some ( v) if v != "action" => Err ( ActionParseFromUrlError :: NotAction ) ,
93- _ => Err ( ActionParseFromUrlError :: Invalid ) ,
94- } ?;
95-
96- let params = url
97- . query_pairs ( )
98- . collect :: < std:: collections:: HashMap < _ , _ > > ( ) ;
99- let json_value = params
100- . get ( "value" )
101- . ok_or ( ActionParseFromUrlError :: Invalid ) ?;
102- let action: Self = serde_json:: from_str ( json_value)
103- . map_err ( |e| ActionParseFromUrlError :: ParseFailed ( e. to_string ( ) ) ) ?;
104- Ok ( action)
64+ Some ( "pause" ) => Ok ( Self :: PauseRecording ) ,
65+ Some ( "resume" ) => Ok ( Self :: ResumeRecording ) ,
66+ Some ( "toggle-pause" ) => Ok ( Self :: TogglePauseRecording ) ,
67+ Some ( "stop" ) => Ok ( Self :: StopRecording ) ,
68+ Some ( "screenshot" ) => Ok ( Self :: TakeScreenshot ) ,
69+ _ => {
70+ if url. domain ( ) == Some ( "action" ) {
71+ let params = url. query_pairs ( ) . collect :: < std:: collections:: HashMap < _ , _ > > ( ) ;
72+ let json_value = params. get ( "value" ) . ok_or ( ActionParseFromUrlError :: Invalid ) ?;
73+ let action: Self = serde_json:: from_str ( json_value)
74+ . map_err ( |e| ActionParseFromUrlError :: ParseFailed ( e. to_string ( ) ) ) ?;
75+ Ok ( action)
76+ } else {
77+ Err ( ActionParseFromUrlError :: Invalid )
78+ }
79+ }
80+ }
10581 }
10682}
10783
10884impl DeepLinkAction {
10985 pub async fn execute ( self , app : & AppHandle ) -> Result < ( ) , String > {
86+ trace ! ( "Executing deep link action: {:?}" , self ) ;
87+
11088 match self {
111- DeepLinkAction :: StartRecording {
89+ Self :: PauseRecording => {
90+ let state = app. state :: < ArcLock < App > > ( ) ;
91+ let mut app_state = state. write ( ) . await ;
92+ if let Some ( recording) = app_state. current_recording_mut ( ) {
93+ recording. pause ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
94+ }
95+ Ok ( ( ) )
96+ }
97+ Self :: ResumeRecording => {
98+ let state = app. state :: < ArcLock < App > > ( ) ;
99+ let mut app_state = state. write ( ) . await ;
100+ if let Some ( recording) = app_state. current_recording_mut ( ) {
101+ recording. resume ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
102+ }
103+ Ok ( ( ) )
104+ }
105+ Self :: TogglePauseRecording => {
106+ let state = app. state :: < ArcLock < App > > ( ) ;
107+ let mut app_state = state. write ( ) . await ;
108+ if let Some ( recording) = app_state. current_recording_mut ( ) {
109+ let is_paused = recording. is_paused ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
110+ if is_paused {
111+ recording. resume ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
112+ } else {
113+ recording. pause ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
114+ }
115+ }
116+ Ok ( ( ) )
117+ }
118+ Self :: StopRecording => {
119+ let state = app. state :: < ArcLock < App > > ( ) ;
120+ let mut app_state = state. write ( ) . await ;
121+ if let Some ( recording) = app_state. current_recording_mut ( ) {
122+ recording. stop ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
123+ }
124+ Ok ( ( ) )
125+ }
126+ Self :: TakeScreenshot => {
127+ app. emit ( "recording-action" , "screenshot" )
128+ . map_err ( |e| e. to_string ( ) ) ?;
129+ Ok ( ( ) )
130+ }
131+ Self :: StartRecording {
112132 capture_mode,
113133 camera,
114134 mic_label,
@@ -117,42 +137,81 @@ impl DeepLinkAction {
117137 } => {
118138 let state = app. state :: < ArcLock < App > > ( ) ;
119139
120- crate :: set_camera_input ( app. clone ( ) , state. clone ( ) , camera, None ) . await ?;
121- crate :: set_mic_input ( state. clone ( ) , mic_label) . await ?;
122-
123- let capture_target: ScreenCaptureTarget = match capture_mode {
124- CaptureMode :: Screen ( name) => cap_recording:: screen_capture:: list_displays ( )
140+ let capture_target = match capture_mode {
141+ CaptureMode :: Screen ( name) => cap_recording:: sources:: screen_capture:: list_displays ( )
125142 . into_iter ( )
126- . find ( |( s, _) | s. name == name)
143+ . find ( |( s, _) | s. name == * name)
127144 . map ( |( s, _) | ScreenCaptureTarget :: Display { id : s. id } )
128145 . ok_or ( format ! ( "No screen with name \" {}\" " , & name) ) ?,
129- CaptureMode :: Window ( name) => cap_recording:: screen_capture:: list_windows ( )
146+ CaptureMode :: Window ( name) => cap_recording:: sources :: screen_capture:: list_windows ( )
130147 . into_iter ( )
131- . find ( |( w, _) | w. name == name)
148+ . find ( |( w, _) | w. name == * name)
132149 . map ( |( w, _) | ScreenCaptureTarget :: Window { id : w. id } )
133150 . ok_or ( format ! ( "No window with name \" {}\" " , & name) ) ?,
134151 } ;
135152
136153 let inputs = StartRecordingInputs {
137- mode,
138154 capture_target,
139155 capture_system_audio,
156+ mode,
140157 organization_id : None ,
141158 } ;
142159
143- crate :: recording:: start_recording ( app. clone ( ) , state, inputs)
160+ if let Some ( camera_id) = camera {
161+ crate :: set_camera_input ( app. clone ( ) , state. clone ( ) , Some ( camera_id. clone ( ) ) , None )
162+ . await
163+ . map_err ( |e| e. to_string ( ) ) ?;
164+ }
165+
166+ if let Some ( mic) = mic_label {
167+ crate :: set_mic_input ( state. clone ( ) , Some ( mic. clone ( ) ) )
168+ . await
169+ . map_err ( |e| e. to_string ( ) ) ?;
170+ }
171+
172+ crate :: recording:: start_recording ( app. clone ( ) , state. clone ( ) , inputs)
144173 . await
145- . map ( |_| ( ) )
174+ . map_err ( |e| e. to_string ( ) ) ?;
175+ Ok ( ( ) )
146176 }
147- DeepLinkAction :: StopRecording => {
148- crate :: recording:: stop_recording ( app. clone ( ) , app. state ( ) ) . await
149- }
150- DeepLinkAction :: OpenEditor { project_path } => {
177+ Self :: OpenEditor { project_path } => {
151178 crate :: open_project_from_path ( Path :: new ( & project_path) , app. clone ( ) )
179+ . map_err ( |e| e. to_string ( ) ) ?;
180+ Ok ( ( ) )
181+ }
182+ Self :: OpenSettings { page } => {
183+ crate :: show_window ( app. clone ( ) , ShowCapWindow :: Settings { page : page. clone ( ) } )
184+ . await
185+ . map_err ( |e| e. to_string ( ) ) ?;
186+ Ok ( ( ) )
152187 }
153- DeepLinkAction :: OpenSettings { page } => {
154- crate :: show_window ( app. clone ( ) , ShowCapWindow :: Settings { page } ) . await
188+ _ => {
189+ trace ! ( "Deep link action not implemented: {:?}" , self ) ;
190+ Ok ( ( ) )
155191 }
156192 }
157193 }
158194}
195+
196+ pub fn handle ( app_handle : & AppHandle , urls : Vec < Url > ) {
197+ trace ! ( "Handling deep actions for: {:?}" , & urls) ;
198+
199+ let actions: Vec < _ > = urls
200+ . into_iter ( )
201+ . filter ( |url| !url. as_str ( ) . is_empty ( ) )
202+ . filter_map ( |url| DeepLinkAction :: try_from ( & url) . ok ( ) )
203+ . collect ( ) ;
204+
205+ if actions. is_empty ( ) {
206+ return ;
207+ }
208+
209+ let app_handle = app_handle. clone ( ) ;
210+ tauri:: async_runtime:: spawn ( async move {
211+ for action in actions {
212+ if let Err ( e) = action. execute ( & app_handle) . await {
213+ trace ! ( "Failed to handle deep link action: {}" , e) ;
214+ }
215+ }
216+ } ) ;
217+ }
0 commit comments