@@ -144,296 +144,6 @@ impl MtpConnectionManager {
144144 } )
145145 }
146146
147- /// Downloads a file or directory recursively from the MTP device to a local path.
148- ///
149- /// # Arguments
150- ///
151- /// * `device_id` - The connected device ID
152- /// * `storage_id` - The storage ID within the device
153- /// * `object_path` - Virtual path on the device to download
154- /// * `local_dest` - Local destination path
155- ///
156- /// # Returns
157- ///
158- /// Total bytes transferred.
159- pub async fn download_recursive (
160- & self ,
161- device_id : & str ,
162- storage_id : u32 ,
163- object_path : & str ,
164- local_dest : & Path ,
165- ) -> Result < u64 , MtpConnectionError > {
166- debug ! (
167- "MTP download_recursive: device={}, storage={}, path={}, dest={}" ,
168- device_id,
169- storage_id,
170- object_path,
171- local_dest. display( )
172- ) ;
173-
174- // Try to list the path as a directory
175- match self . list_directory ( device_id, storage_id, object_path) . await {
176- Ok ( entries) if !entries. is_empty ( ) => {
177- // Directory with contents — download all entries
178- self . download_entries_recursive ( device_id, storage_id, & entries, local_dest)
179- . await
180- }
181- Ok ( _) => {
182- // Empty result — disambiguate file vs empty directory
183- if self . is_file_by_parent ( device_id, storage_id, object_path) . await {
184- debug ! ( "MTP download_recursive: {} is a file, downloading" , object_path) ;
185- let operation_id = format ! ( "download-{}" , uuid:: Uuid :: new_v4( ) ) ;
186- let result = self
187- . download_file ( device_id, storage_id, object_path, local_dest, None , & operation_id)
188- . await ?;
189- Ok ( result. bytes_transferred )
190- } else {
191- debug ! ( "MTP download_recursive: {} is an empty directory" , object_path) ;
192- tokio:: fs:: create_dir_all ( local_dest)
193- . await
194- . map_err ( |e| MtpConnectionError :: Other {
195- device_id : device_id. to_string ( ) ,
196- message : format ! ( "Failed to create local directory: {}" , e) ,
197- } ) ?;
198- Ok ( 0 )
199- }
200- }
201- Err ( e) => {
202- // list_directory failed — check if it's a file
203- debug ! (
204- "MTP download_recursive: list failed for '{}', checking if it's a file: {:?}" ,
205- object_path, e
206- ) ;
207- if self . is_file_by_parent ( device_id, storage_id, object_path) . await {
208- debug ! ( "MTP download_recursive: {} is a file, downloading" , object_path) ;
209- let operation_id = format ! ( "download-{}" , uuid:: Uuid :: new_v4( ) ) ;
210- let result = self
211- . download_file ( device_id, storage_id, object_path, local_dest, None , & operation_id)
212- . await ?;
213- Ok ( result. bytes_transferred )
214- } else {
215- Err ( e)
216- }
217- }
218- }
219- }
220-
221- /// Downloads a list of already-known directory entries to a local path.
222- ///
223- /// Like `scan_entries_recursive`, this takes entries from a parent listing,
224- /// so files are handled directly and directories get one `list_directory` call each.
225- async fn download_entries_recursive (
226- & self ,
227- device_id : & str ,
228- storage_id : u32 ,
229- entries : & [ FileEntry ] ,
230- local_dest : & Path ,
231- ) -> Result < u64 , MtpConnectionError > {
232- tokio:: fs:: create_dir_all ( local_dest)
233- . await
234- . map_err ( |e| MtpConnectionError :: Other {
235- device_id : device_id. to_string ( ) ,
236- message : format ! ( "Failed to create local directory: {}" , e) ,
237- } ) ?;
238-
239- let mut total_bytes = 0u64 ;
240-
241- for entry in entries {
242- let child_dest = local_dest. join ( & entry. name ) ;
243-
244- if entry. is_directory {
245- let children = self . list_directory ( device_id, storage_id, & entry. path ) . await ?;
246- if children. is_empty ( ) {
247- tokio:: fs:: create_dir_all ( & child_dest)
248- . await
249- . map_err ( |e| MtpConnectionError :: Other {
250- device_id : device_id. to_string ( ) ,
251- message : format ! ( "Failed to create local directory: {}" , e) ,
252- } ) ?;
253- } else {
254- let bytes =
255- Box :: pin ( self . download_entries_recursive ( device_id, storage_id, & children, & child_dest) )
256- . await ?;
257- total_bytes += bytes;
258- }
259- } else {
260- let operation_id = format ! ( "download-{}" , uuid:: Uuid :: new_v4( ) ) ;
261- let result = self
262- . download_file ( device_id, storage_id, & entry. path , & child_dest, None , & operation_id)
263- . await ?;
264- total_bytes += result. bytes_transferred ;
265- }
266- }
267-
268- debug ! (
269- "MTP download_entries_recursive: directory {} complete, {} bytes" ,
270- local_dest. display( ) ,
271- total_bytes
272- ) ;
273- Ok ( total_bytes)
274- }
275-
276- /// Downloads a file or directory recursively with a progress/cancellation callback.
277- ///
278- /// Same as `download_recursive` but calls `on_progress(bytes_done, bytes_total)` per chunk.
279- /// Returns `ControlFlow::Break(())` from the callback to cancel.
280- pub async fn download_recursive_with_progress (
281- & self ,
282- device_id : & str ,
283- storage_id : u32 ,
284- object_path : & str ,
285- local_dest : & Path ,
286- on_progress : & ( dyn Fn ( u64 , u64 ) -> std:: ops:: ControlFlow < ( ) > + Send + Sync ) ,
287- ) -> Result < u64 , MtpConnectionError > {
288- debug ! (
289- "MTP download_recursive_with_progress: device={}, storage={}, path={}, dest={}" ,
290- device_id,
291- storage_id,
292- object_path,
293- local_dest. display( )
294- ) ;
295-
296- match self . list_directory ( device_id, storage_id, object_path) . await {
297- Ok ( entries) if !entries. is_empty ( ) => {
298- self . download_entries_recursive_with_progress ( device_id, storage_id, & entries, local_dest, on_progress)
299- . await
300- }
301- Ok ( _) => {
302- if self . is_file_by_parent ( device_id, storage_id, object_path) . await {
303- let operation_id = format ! ( "download-{}" , uuid:: Uuid :: new_v4( ) ) ;
304- let result = self
305- . download_file_with_progress (
306- device_id,
307- storage_id,
308- object_path,
309- local_dest,
310- None ,
311- & operation_id,
312- Some ( on_progress) ,
313- )
314- . await ?;
315- Ok ( result. bytes_transferred )
316- } else {
317- tokio:: fs:: create_dir_all ( local_dest)
318- . await
319- . map_err ( |e| MtpConnectionError :: Other {
320- device_id : device_id. to_string ( ) ,
321- message : format ! ( "Failed to create local directory: {}" , e) ,
322- } ) ?;
323- Ok ( 0 )
324- }
325- }
326- Err ( e) => {
327- if self . is_file_by_parent ( device_id, storage_id, object_path) . await {
328- let operation_id = format ! ( "download-{}" , uuid:: Uuid :: new_v4( ) ) ;
329- let result = self
330- . download_file_with_progress (
331- device_id,
332- storage_id,
333- object_path,
334- local_dest,
335- None ,
336- & operation_id,
337- Some ( on_progress) ,
338- )
339- . await ?;
340- Ok ( result. bytes_transferred )
341- } else {
342- Err ( e)
343- }
344- }
345- }
346- }
347-
348- /// Downloads directory entries recursively with a progress/cancellation callback.
349- async fn download_entries_recursive_with_progress (
350- & self ,
351- device_id : & str ,
352- storage_id : u32 ,
353- entries : & [ FileEntry ] ,
354- local_dest : & Path ,
355- on_progress : & ( dyn Fn ( u64 , u64 ) -> std:: ops:: ControlFlow < ( ) > + Send + Sync ) ,
356- ) -> Result < u64 , MtpConnectionError > {
357- tokio:: fs:: create_dir_all ( local_dest)
358- . await
359- . map_err ( |e| MtpConnectionError :: Other {
360- device_id : device_id. to_string ( ) ,
361- message : format ! ( "Failed to create local directory: {}" , e) ,
362- } ) ?;
363-
364- let mut total_bytes = 0u64 ;
365-
366- for entry in entries {
367- let child_dest = local_dest. join ( & entry. name ) ;
368-
369- if entry. is_directory {
370- let children = self . list_directory ( device_id, storage_id, & entry. path ) . await ?;
371- if children. is_empty ( ) {
372- tokio:: fs:: create_dir_all ( & child_dest)
373- . await
374- . map_err ( |e| MtpConnectionError :: Other {
375- device_id : device_id. to_string ( ) ,
376- message : format ! ( "Failed to create local directory: {}" , e) ,
377- } ) ?;
378- } else {
379- let bytes = Box :: pin ( self . download_entries_recursive_with_progress (
380- device_id,
381- storage_id,
382- & children,
383- & child_dest,
384- on_progress,
385- ) )
386- . await ?;
387- total_bytes += bytes;
388- }
389- } else {
390- let operation_id = format ! ( "download-{}" , uuid:: Uuid :: new_v4( ) ) ;
391- let result = self
392- . download_file_with_progress (
393- device_id,
394- storage_id,
395- & entry. path ,
396- & child_dest,
397- None ,
398- & operation_id,
399- Some ( on_progress) ,
400- )
401- . await ?;
402- total_bytes += result. bytes_transferred ;
403- }
404- }
405-
406- debug ! (
407- "MTP download_entries_recursive_with_progress: directory {} complete, {} bytes" ,
408- local_dest. display( ) ,
409- total_bytes
410- ) ;
411- Ok ( total_bytes)
412- }
413-
414- /// Checks if a path is a file by looking it up in its parent directory listing.
415- async fn is_file_by_parent ( & self , device_id : & str , storage_id : u32 , path : & str ) -> bool {
416- let path_buf = normalize_mtp_path ( path) ;
417- let Some ( parent) = path_buf. parent ( ) else {
418- return false ;
419- } ;
420- let Some ( name) = path_buf. file_name ( ) . and_then ( |n| n. to_str ( ) ) else {
421- return false ;
422- } ;
423-
424- if let Ok ( parent_entries) = self
425- . list_directory ( device_id, storage_id, & parent. to_string_lossy ( ) )
426- . await
427- {
428- parent_entries
429- . iter ( )
430- . find ( |e| e. name == name)
431- . is_some_and ( |e| !e. is_directory )
432- } else {
433- false
434- }
435- }
436-
437147 /// Uploads a file or directory from local filesystem to MTP device recursively.
438148 ///
439149 /// If the source is a directory, creates the directory on the device and
0 commit comments