@@ -906,6 +906,7 @@ pub async fn provider_pwd(state: State<'_, ProviderState>) -> Result<String, Str
906906}
907907
908908/// Download a file from the remote server
909+ #[ allow( clippy:: too_many_arguments) ]
909910#[ tauri:: command]
910911pub async fn provider_download_file (
911912 app : AppHandle ,
@@ -914,6 +915,7 @@ pub async fn provider_download_file(
914915 local_path : String ,
915916 modified : Option < String > ,
916917 use_delta : Option < bool > ,
918+ download_segments : Option < u32 > ,
917919) -> Result < String , String > {
918920 let mut provider_lock = state. provider . lock ( ) . await ;
919921
@@ -960,7 +962,7 @@ pub async fn provider_download_file(
960962 let fname_progress = filename. clone ( ) ;
961963
962964 let dl_start_time = std:: time:: Instant :: now ( ) ;
963- let progress_cb: Option < Box < dyn Fn ( u64 , u64 ) + Send > > = if file_size > 0 {
965+ let mut progress_cb: Option < Box < dyn Fn ( u64 , u64 ) + Send > > = if file_size > 0 {
964966 Some ( Box :: new ( move |transferred : u64 , total : u64 | {
965967 let pct = if total > 0 {
966968 ( ( transferred as f64 / total as f64 ) * 100.0 ) as u8
@@ -1090,25 +1092,122 @@ pub async fn provider_download_file(
10901092 // Resume-aware download: if provider supports resume and a partial .aerotmp exists,
10911093 // use resume_download to continue from where we left off. This avoids re-downloading
10921094 // data on S3/Azure (pay-per-GB) and all other HTTP-based providers.
1093- let result = if provider . supports_resume ( ) {
1094- let tmp_path = format ! ( "{}.aerotmp" , local_path ) ;
1095- let offset = tokio:: fs:: metadata ( & tmp_path)
1095+ let tmp_path = format ! ( "{}.aerotmp" , local_path ) ;
1096+ let partial_offset = if provider . supports_resume ( ) {
1097+ tokio:: fs:: metadata ( & tmp_path)
10961098 . await
10971099 . map ( |m| m. len ( ) )
1098- . unwrap_or ( 0 ) ;
1099- if offset > 0 {
1100- info ! (
1101- "Resuming download from offset {} bytes: {}" ,
1102- offset, filename
1103- ) ;
1104- provider
1105- . resume_download ( & remote_path, & local_path, offset, progress_cb)
1106- . await
1107- } else {
1108- provider
1109- . download ( & remote_path, & local_path, progress_cb)
1110- . await
1100+ . unwrap_or ( 0 )
1101+ } else {
1102+ 0
1103+ } ;
1104+
1105+ // GTC-2: opportunistic intra-file range parallelism on the single-file
1106+ // path. Gated on no-resume (`partial_offset == 0`) because the segmented
1107+ // engine pre-allocates and overwrites its own `.aerotmp`; a partial
1108+ // legacy resume must not be silently dropped. On hard failure we fall
1109+ // through to the legacy single-stream branch below.
1110+ let mut segmented_result: Option < Result < ( ) , String > > = None ;
1111+ if partial_offset == 0 {
1112+ if let Some ( requested) = download_segments {
1113+ if let Some ( segments) =
1114+ crate :: provider_transfer_executor:: provider_segmented_download_eligible (
1115+ provider. as_ref ( ) ,
1116+ file_size,
1117+ requested,
1118+ requested as usize ,
1119+ )
1120+ {
1121+ info ! (
1122+ "Segmented download: {} segments on {} ({} bytes)" ,
1123+ segments, filename, file_size
1124+ ) ;
1125+ let cancel = tokio_util:: sync:: CancellationToken :: new ( ) ;
1126+ let outcome =
1127+ crate :: provider_transfer_executor:: run_provider_segmented_download (
1128+ provider. as_ref ( ) ,
1129+ & remote_path,
1130+ & local_path,
1131+ file_size,
1132+ segments,
1133+ progress_cb. take ( ) ,
1134+ cancel,
1135+ )
1136+ . await ;
1137+ if let Err ( ref e) = outcome {
1138+ warn ! (
1139+ "Segmented download failed, falling back to single-stream: {}" ,
1140+ e
1141+ ) ;
1142+ }
1143+ segmented_result = Some ( outcome) ;
1144+ }
11111145 }
1146+ }
1147+
1148+ // If segmented ran (success or hard error) the original progress_cb has
1149+ // been moved into it. Build a fresh callback for the legacy fallback so
1150+ // the user still sees per-byte progress.
1151+ if segmented_result. is_some ( ) && progress_cb. is_none ( ) && file_size > 0 {
1152+ let app_progress_fb = app. clone ( ) ;
1153+ let tid_fb = transfer_id. clone ( ) ;
1154+ let fname_fb = filename. clone ( ) ;
1155+ let start_fb = std:: time:: Instant :: now ( ) ;
1156+ progress_cb = Some ( Box :: new ( move |transferred : u64 , total : u64 | {
1157+ let pct = if total > 0 {
1158+ ( ( transferred as f64 / total as f64 ) * 100.0 ) as u8
1159+ } else {
1160+ 0
1161+ } ;
1162+ let elapsed = start_fb. elapsed ( ) . as_secs_f64 ( ) ;
1163+ let speed = if elapsed > 0.1 {
1164+ ( transferred as f64 / elapsed) as u64
1165+ } else {
1166+ 0
1167+ } ;
1168+ let eta = if speed > 0 && transferred < total {
1169+ ( ( total - transferred) as f64 / speed as f64 ) as u64
1170+ } else {
1171+ 0
1172+ } ;
1173+ let _ = app_progress_fb. emit (
1174+ "transfer_event" ,
1175+ crate :: TransferEvent {
1176+ event_type : "progress" . to_string ( ) ,
1177+ transfer_id : tid_fb. clone ( ) ,
1178+ filename : fname_fb. clone ( ) ,
1179+ direction : "download" . to_string ( ) ,
1180+ message : None ,
1181+ progress : Some ( crate :: TransferProgress {
1182+ transfer_id : tid_fb. clone ( ) ,
1183+ filename : fname_fb. clone ( ) ,
1184+ direction : "download" . to_string ( ) ,
1185+ percentage : pct,
1186+ transferred,
1187+ total,
1188+ speed_bps : speed,
1189+ eta_seconds : eta as u32 ,
1190+ total_files : None ,
1191+ path : None ,
1192+ } ) ,
1193+ path : None ,
1194+ delta_stats : None ,
1195+ fallback_reason : None ,
1196+ } ,
1197+ ) ;
1198+ } ) ) ;
1199+ }
1200+
1201+ let result = if let Some ( Ok ( ( ) ) ) = & segmented_result {
1202+ Ok ( ( ) )
1203+ } else if partial_offset > 0 {
1204+ info ! (
1205+ "Resuming download from offset {} bytes: {}" ,
1206+ partial_offset, filename
1207+ ) ;
1208+ provider
1209+ . resume_download ( & remote_path, & local_path, partial_offset, progress_cb)
1210+ . await
11121211 } else {
11131212 provider
11141213 . download ( & remote_path, & local_path, progress_cb)
@@ -1179,11 +1278,13 @@ pub async fn provider_download_folder(
11791278 max_concurrent : Option < u32 > ,
11801279 retry_count : Option < u32 > ,
11811280 timeout_seconds : Option < u64 > ,
1281+ download_segments : Option < u32 > ,
11821282) -> Result < String , String > {
11831283 let runtime_settings = resolve_provider_transfer_settings ( TransferSettingsInput {
11841284 max_concurrent,
11851285 retry_count,
11861286 timeout_seconds,
1287+ download_segments,
11871288 } ) ;
11881289
11891290 // Capture current pwd so we can restore it after folder scan changes it
@@ -1236,6 +1337,10 @@ pub async fn provider_upload_folder(
12361337 max_concurrent,
12371338 retry_count,
12381339 timeout_seconds,
1340+ // Upload-side intra-file parallelism is a separate slice (out
1341+ // of scope for GTC-1); upload paths keep single-stream legacy
1342+ // behaviour regardless of the requested segments knob.
1343+ download_segments : None ,
12391344 } ) ;
12401345
12411346 // Capture current pwd so we can restore it after upload
0 commit comments