@@ -175,7 +175,9 @@ async fn resolve_engine_for_model(
175175 match engine_hint. map ( |e| e. to_lowercase ( ) ) {
176176 Some ( ref engine) if engine == "soniox" => {
177177 if crate :: secure_store:: secure_has ( app, "stt_api_key_soniox" ) . unwrap_or ( false ) {
178- Ok ( ActiveEngineSelection :: Soniox { model_name : model_name. to_string ( ) } )
178+ Ok ( ActiveEngineSelection :: Soniox {
179+ model_name : model_name. to_string ( ) ,
180+ } )
179181 } else {
180182 Err ( "Soniox token not configured. Please configure it in Models." . to_string ( ) )
181183 }
@@ -216,9 +218,13 @@ async fn resolve_engine_for_model(
216218 None => {
217219 if model_name == "soniox" {
218220 if crate :: secure_store:: secure_has ( app, "stt_api_key_soniox" ) . unwrap_or ( false ) {
219- return Ok ( ActiveEngineSelection :: Soniox { model_name : model_name. to_string ( ) } ) ;
221+ return Ok ( ActiveEngineSelection :: Soniox {
222+ model_name : model_name. to_string ( ) ,
223+ } ) ;
220224 } else {
221- return Err ( "Soniox token not configured. Please configure it in Models." . to_string ( ) ) ;
225+ return Err (
226+ "Soniox token not configured. Please configure it in Models." . to_string ( ) ,
227+ ) ;
222228 }
223229 }
224230 if let Some ( path) = whisper_state. read ( ) . await . get_model_path ( model_name) {
@@ -354,7 +360,8 @@ async fn validate_recording_requirements(app: &AppHandle) -> Result<(), String>
354360 . and_then ( |v| v. as_str ( ) . map ( |s| s. to_string ( ) ) )
355361 . unwrap_or_else ( || "whisper" . to_string ( ) ) ;
356362 if engine == "soniox" {
357- let has_key = crate :: secure_store:: secure_has ( app, "stt_api_key_soniox" ) . unwrap_or ( false ) ;
363+ let has_key =
364+ crate :: secure_store:: secure_has ( app, "stt_api_key_soniox" ) . unwrap_or ( false ) ;
358365 ( true , has_key)
359366 } else {
360367 ( false , false )
@@ -364,7 +371,8 @@ async fn validate_recording_requirements(app: &AppHandle) -> Result<(), String>
364371 }
365372 } ;
366373
367- let has_models = has_whisper_models || has_parakeet_models || ( is_soniox_selected && soniox_ready) ;
374+ let has_models =
375+ has_whisper_models || has_parakeet_models || ( is_soniox_selected && soniox_ready) ;
368376
369377 if !has_models {
370378 log:: error!( "No models downloaded" ) ;
@@ -375,11 +383,15 @@ async fn validate_recording_requirements(app: &AppHandle) -> Result<(), String>
375383 "no-models-error" ,
376384 serde_json:: json!( {
377385 "title" : "No Speech Recognition Models" ,
378- "message" : if is_soniox_selected { "Please configure your Soniox token in Settings before recording." } else { "Please download at least one model from Settings before recording." } ,
386+ "message" : if is_soniox_selected { "Please configure your Soniox token in Models before recording." } else { "Please download at least one model from Models before recording." } ,
379387 "action" : "open-settings"
380388 } ) ,
381389 ) ;
382- return Err ( if is_soniox_selected { "Soniox token missing" . to_string ( ) } else { "No speech recognition models installed. Please download a model first." . to_string ( ) } ) ;
390+ return Err ( if is_soniox_selected {
391+ "Soniox token missing" . to_string ( )
392+ } else {
393+ "No speech recognition models installed. Please download a model first." . to_string ( )
394+ } ) ;
383395 }
384396
385397 // Check license status (with caching to improve performance)
@@ -1054,12 +1066,14 @@ pub async fn stop_recording(
10541066 & app,
10551067 & audio_path,
10561068 "Soniox token not configured" ,
1057- "Please configure your Soniox token in Settings before recording." ,
1069+ "Please configure your Soniox token in Models before recording." ,
10581070 )
10591071 . await ;
10601072 }
10611073
1062- ActiveEngineSelection :: Soniox { model_name : config. current_model . clone ( ) }
1074+ ActiveEngineSelection :: Soniox {
1075+ model_name : config. current_model . clone ( ) ,
1076+ }
10631077 }
10641078 _ => {
10651079 let downloaded_models = whisper_manager. read ( ) . await . get_downloaded_model_names ( ) ;
@@ -1070,7 +1084,7 @@ pub async fn stop_recording(
10701084 & app,
10711085 & audio_path,
10721086 "No speech recognition models installed" ,
1073- "Please download at least one speech recognition model from Settings to use VoiceTypr." ,
1087+ "Please download at least one speech recognition model from Models to use VoiceTypr." ,
10741088 )
10751089 . await ;
10761090 }
@@ -1184,7 +1198,9 @@ pub async fn stop_recording(
11841198 let normalized_path = {
11851199 let ts = chrono:: Local :: now ( ) . format ( "%Y%m%d_%H%M%S" ) ;
11861200 let out_path = parent_dir. join ( format ! ( "normalized_{}.wav" , ts) ) ;
1187- if let Err ( e) = crate :: ffmpeg:: normalize_streaming ( & app, & audio_path, & out_path) . await {
1201+ if let Err ( e) =
1202+ crate :: ffmpeg:: normalize_streaming ( & app, & audio_path, & out_path) . await
1203+ {
11881204 log:: error!( "Audio normalization (ffmpeg) failed: {}" , e) ;
11891205 update_recording_state (
11901206 & app,
@@ -1277,7 +1293,6 @@ pub async fn stop_recording(
12771293 config. ai_enabled
12781294 ) ;
12791295
1280-
12811296 let language = if config. language . is_empty ( ) {
12821297 None
12831298 } else {
@@ -1431,7 +1446,13 @@ pub async fn stop_recording(
14311446 }
14321447 }
14331448 ActiveEngineSelection :: Soniox { .. } => {
1434- match soniox_transcribe_async ( & app_for_task, & audio_path_clone, language_for_task. as_deref ( ) ) . await {
1449+ match soniox_transcribe_async (
1450+ & app_for_task,
1451+ & audio_path_clone,
1452+ language_for_task. as_deref ( ) ,
1453+ )
1454+ . await
1455+ {
14351456 Ok ( text) => Ok ( text) ,
14361457 Err ( e) => Err ( e) ,
14371458 }
@@ -1807,19 +1828,34 @@ pub async fn save_transcription(app: AppHandle, text: String, model: String) ->
18071828 if let Some ( value) = store. get ( & key) {
18081829 match & latest {
18091830 Some ( ( ts, _) ) => {
1810- if key > * ts { latest = Some ( ( key. to_string ( ) , value) ) ; }
1831+ if key > * ts {
1832+ latest = Some ( ( key. to_string ( ) , value) ) ;
1833+ }
18111834 }
1812- None => latest = Some ( ( key. to_string ( ) , value) )
1835+ None => latest = Some ( ( key. to_string ( ) , value) ) ,
18131836 }
18141837 }
18151838 }
18161839
18171840 if let Some ( ( ts, v) ) = latest {
1818- let same_text = v. get ( "text" ) . and_then ( |x| x. as_str ( ) ) . map ( |s| s == text) . unwrap_or ( false ) ;
1819- let same_model = v. get ( "model" ) . and_then ( |x| x. as_str ( ) ) . map ( |s| s == model) . unwrap_or ( false ) ;
1841+ let same_text = v
1842+ . get ( "text" )
1843+ . and_then ( |x| x. as_str ( ) )
1844+ . map ( |s| s == text)
1845+ . unwrap_or ( false ) ;
1846+ let same_model = v
1847+ . get ( "model" )
1848+ . and_then ( |x| x. as_str ( ) )
1849+ . map ( |s| s == model)
1850+ . unwrap_or ( false ) ;
18201851 let within_window = chrono:: DateTime :: parse_from_rfc3339 ( & ts)
18211852 . ok ( )
1822- . and_then ( |t| t. with_timezone ( & chrono:: Utc ) . signed_duration_since ( chrono:: Utc :: now ( ) ) . num_seconds ( ) . checked_abs ( ) )
1853+ . and_then ( |t| {
1854+ t. with_timezone ( & chrono:: Utc )
1855+ . signed_duration_since ( chrono:: Utc :: now ( ) )
1856+ . num_seconds ( )
1857+ . checked_abs ( )
1858+ } )
18231859 . map ( |secs| secs <= 2 )
18241860 . unwrap_or ( false ) ;
18251861 if same_text && same_model && within_window {
@@ -2145,9 +2181,13 @@ pub async fn transcribe_audio(
21452181}
21462182
21472183// Soniox async transcription via v1 Files + Transcriptions flow
2148- async fn soniox_transcribe_async ( app : & AppHandle , wav_path : & Path , language : Option < & str > ) -> Result < String , String > {
2149- use tokio:: fs;
2184+ async fn soniox_transcribe_async (
2185+ app : & AppHandle ,
2186+ wav_path : & Path ,
2187+ language : Option < & str > ,
2188+ ) -> Result < String , String > {
21502189 use reqwest:: multipart:: { Form , Part } ;
2190+ use tokio:: fs;
21512191
21522192 let key = crate :: secure_store:: secure_get ( app, "stt_api_key_soniox" ) ?
21532193 . ok_or_else ( || "Soniox API key not set" . to_string ( ) ) ?;
@@ -2160,10 +2200,14 @@ async fn soniox_transcribe_async(app: &AppHandle, wav_path: &Path, language: Opt
21602200 let base = "https://api.soniox.com/v1" ;
21612201
21622202 // 1) Upload file -> file_id
2163- let filename = wav_path. file_name ( ) . and_then ( |s| s. to_str ( ) ) . unwrap_or ( "audio.wav" ) ;
2203+ let filename = wav_path
2204+ . file_name ( )
2205+ . and_then ( |s| s. to_str ( ) )
2206+ . unwrap_or ( "audio.wav" ) ;
21642207 let file_part = Part :: bytes ( wav_bytes)
21652208 . file_name ( filename. to_string ( ) )
2166- . mime_str ( "audio/wav" ) . map_err ( |e| e. to_string ( ) ) ?;
2209+ . mime_str ( "audio/wav" )
2210+ . map_err ( |e| e. to_string ( ) ) ?;
21672211 let form = Form :: new ( ) . part ( "file" , file_part) ;
21682212
21692213 let upload_url = format ! ( "{}/files" , base) ;
@@ -2181,14 +2225,20 @@ async fn soniox_transcribe_async(app: &AppHandle, wav_path: &Path, language: Opt
21812225 return Err ( format ! ( "Soniox upload failed: HTTP {}: {}" , code, snippet) ) ;
21822226 }
21832227 let upload_json: serde_json:: Value = upload_resp. json ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
2184- let file_id = upload_json. get ( "id" ) . and_then ( |v| v. as_str ( ) ) . ok_or ( "Missing file_id" ) ?. to_string ( ) ;
2228+ let file_id = upload_json
2229+ . get ( "id" )
2230+ . and_then ( |v| v. as_str ( ) )
2231+ . ok_or ( "Missing file_id" ) ?
2232+ . to_string ( ) ;
21852233
21862234 // 2) Create transcription -> transcription_id
21872235 let mut payload = serde_json:: json!( {
21882236 "model" : "stt-async-preview" ,
21892237 "file_id" : file_id,
21902238 } ) ;
2191- if let Some ( lang) = language { payload[ "language_hints" ] = serde_json:: json!( [ lang] ) ; }
2239+ if let Some ( lang) = language {
2240+ payload[ "language_hints" ] = serde_json:: json!( [ lang] ) ;
2241+ }
21922242
21932243 let create_url = format ! ( "{}/transcriptions" , base) ;
21942244 let create_resp = client
@@ -2203,10 +2253,17 @@ async fn soniox_transcribe_async(app: &AppHandle, wav_path: &Path, language: Opt
22032253 let code = create_resp. status ( ) ;
22042254 let body = create_resp. text ( ) . await . unwrap_or_default ( ) ;
22052255 let snippet: String = body. chars ( ) . take ( 300 ) . collect ( ) ;
2206- return Err ( format ! ( "Soniox create transcription failed: HTTP {}: {}" , code, snippet) ) ;
2256+ return Err ( format ! (
2257+ "Soniox create transcription failed: HTTP {}: {}" ,
2258+ code, snippet
2259+ ) ) ;
22072260 }
22082261 let create_json: serde_json:: Value = create_resp. json ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
2209- let transcription_id = create_json. get ( "id" ) . and_then ( |v| v. as_str ( ) ) . ok_or ( "Missing transcription id" ) ?. to_string ( ) ;
2262+ let transcription_id = create_json
2263+ . get ( "id" )
2264+ . and_then ( |v| v. as_str ( ) )
2265+ . ok_or ( "Missing transcription id" ) ?
2266+ . to_string ( ) ;
22102267
22112268 // 3) Poll status
22122269 let status_url = format ! ( "{}/transcriptions/{}" , base, transcription_id) ;
@@ -2230,11 +2287,16 @@ async fn soniox_transcribe_async(app: &AppHandle, wav_path: &Path, language: Opt
22302287 match status {
22312288 "completed" => break ,
22322289 "error" => {
2233- let msg = json. get ( "error_message" ) . and_then ( |v| v. as_str ( ) ) . unwrap_or ( "Job failed" ) ;
2290+ let msg = json
2291+ . get ( "error_message" )
2292+ . and_then ( |v| v. as_str ( ) )
2293+ . unwrap_or ( "Job failed" ) ;
22342294 return Err ( format ! ( "Soniox job failed: {}" , msg) ) ;
22352295 }
22362296 _ => {
2237- if started. elapsed ( ) > timeout { return Err ( "Soniox transcription timed out" . to_string ( ) ) ; }
2297+ if started. elapsed ( ) > timeout {
2298+ return Err ( "Soniox transcription timed out" . to_string ( ) ) ;
2299+ }
22382300 tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 1000 ) ) . await ;
22392301 }
22402302 }
@@ -2252,7 +2314,10 @@ async fn soniox_transcribe_async(app: &AppHandle, wav_path: &Path, language: Opt
22522314 let code = resp. status ( ) ;
22532315 let body = resp. text ( ) . await . unwrap_or_default ( ) ;
22542316 let snippet: String = body. chars ( ) . take ( 200 ) . collect ( ) ;
2255- return Err ( format ! ( "Soniox transcript failed: HTTP {}: {}" , code, snippet) ) ;
2317+ return Err ( format ! (
2318+ "Soniox transcript failed: HTTP {}: {}" ,
2319+ code, snippet
2320+ ) ) ;
22562321 }
22572322 let json: serde_json:: Value = resp. json ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
22582323
@@ -2265,11 +2330,17 @@ async fn soniox_transcribe_async(app: &AppHandle, wav_path: &Path, language: Opt
22652330 let mut first = true ;
22662331 for t in tokens {
22672332 if let Some ( txt) = t. get ( "text" ) . and_then ( |v| v. as_str ( ) ) {
2268- if !first { out. push ( ' ' ) ; } else { first = false ; }
2333+ if !first {
2334+ out. push ( ' ' ) ;
2335+ } else {
2336+ first = false ;
2337+ }
22692338 out. push_str ( txt) ;
22702339 }
22712340 }
2272- if !out. is_empty ( ) { return Ok ( out) ; }
2341+ if !out. is_empty ( ) {
2342+ return Ok ( out) ;
2343+ }
22732344 }
22742345 Err ( "Soniox transcript format not recognized" . to_string ( ) )
22752346}
0 commit comments