11use indicatif:: { ProgressBar , ProgressState , ProgressStyle } ;
22use std:: {
3+ collections:: HashMap ,
34 fmt:: Write ,
45 io:: SeekFrom ,
56 sync:: { atomic:: AtomicU64 , Arc } ,
67} ;
78use tokio:: {
8- fs:: File ,
9+ fs:: { create_dir_all , File } ,
910 io:: { AsyncSeekExt , AsyncWriteExt } ,
1011} ;
1112use tracing:: trace;
@@ -33,7 +34,7 @@ pub async fn download_file(filename: &str, out_file: Option<String>) -> anyhow::
3334
3435 let add_torrent_result = session. add_torrent ( filename. into ( ) ) . await ?;
3536 let torrent = add_torrent_result. torrent . clone ( ) ;
36- let torrent_meta = add_torrent_result. torrent_meta ;
37+ let _torrent_meta = add_torrent_result. torrent_meta ;
3738
3839 let total_size = torrent. length as u64 ;
3940 let pb = ProgressBar :: new ( total_size) ;
@@ -47,13 +48,41 @@ pub async fn download_file(filename: &str, out_file: Option<String>) -> anyhow::
4748 ) . progress_chars ( "#>-" )
4849 ) ;
4950
50- let out_filename = match out_file {
51+ // Determine the output directory
52+ let output_dir = match out_file {
5153 Some ( name) => name,
52- None => torrent_meta . clone ( ) . torrent_file . info . name . clone ( ) ,
54+ None => torrent . name . clone ( ) ,
5355 } ;
54- let mut file = File :: create ( out_filename) . await ?;
5556
56- // File
57+ // Create output directory and prepare file handles
58+ let mut file_handles: HashMap < usize , File > = HashMap :: new ( ) ;
59+
60+ // Create directories and prepare files for multi-file torrents
61+ for ( file_index, file_info) in torrent. files . iter ( ) . enumerate ( ) {
62+ let file_path = if torrent. files . len ( ) == 1 {
63+ // Single file torrent - use output_dir as filename
64+ std:: path:: PathBuf :: from ( & output_dir)
65+ } else {
66+ // Multi-file torrent - create subdirectory structure
67+ let mut path = std:: path:: PathBuf :: from ( & output_dir) ;
68+ for component in & file_info. path {
69+ path. push ( component) ;
70+ }
71+ path
72+ } ;
73+
74+ // Create parent directories if needed
75+ if let Some ( parent) = file_path. parent ( ) {
76+ create_dir_all ( parent) . await ?;
77+ }
78+
79+ // Create the file
80+ let file = File :: create ( & file_path) . await ?;
81+ file_handles. insert ( file_index, file) ;
82+
83+ trace ! ( "Created file: {:?}" , file_path) ;
84+ }
85+
5786 let total_downloaded = Arc :: new ( AtomicU64 :: new ( 0 ) ) ;
5887 let total_downloaded_clone = total_downloaded. clone ( ) ;
5988
@@ -71,21 +100,41 @@ pub async fn download_file(filename: &str, out_file: Option<String>) -> anyhow::
71100 let pr = add_torrent_result. pr_rx . recv_async ( ) . await ?;
72101
73102 hashset. insert ( pr. index ) ;
74- let ( start, end) = utils:: calculate_bounds_for_piece ( & torrent, pr. index as usize ) ;
75- trace ! (
76- "index: {}, start: {}, end: {} len {}" ,
77- pr. index,
78- start,
79- end,
80- pr. length
81- ) ;
82- file. seek ( SeekFrom :: Start ( start as u64 ) ) . await ?;
83- file. write_all ( pr. buf . as_slice ( ) ) . await ?;
103+
104+ // Map piece to files and write data accordingly
105+ let file_mappings = utils:: map_piece_to_files ( & torrent, pr. index as usize ) ;
106+ let mut piece_offset = 0 ;
107+
108+ for mapping in file_mappings {
109+ let file = file_handles. get_mut ( & mapping. file_index ) . ok_or_else ( || {
110+ anyhow:: anyhow!( "File handle not found for index {}" , mapping. file_index)
111+ } ) ?;
112+
113+ // Seek to correct position in file
114+ file. seek ( SeekFrom :: Start ( mapping. file_offset as u64 ) )
115+ . await ?;
116+
117+ // Write the portion of the piece that belongs to this file
118+ let piece_data = & pr. buf [ piece_offset..piece_offset + mapping. length ] ;
119+ file. write_all ( piece_data) . await ?;
120+
121+ piece_offset += mapping. length ;
122+
123+ trace ! (
124+ "Wrote {} bytes to file {} at offset {}" ,
125+ mapping. length,
126+ mapping. file_index,
127+ mapping. file_offset
128+ ) ;
129+ }
84130
85131 total_downloaded. fetch_add ( pr. length as u64 , std:: sync:: atomic:: Ordering :: Relaxed ) ;
86132 }
87133
88- file. sync_all ( ) . await ?;
134+ // Sync all files
135+ for ( _, file) in file_handles {
136+ file. sync_all ( ) . await ?;
137+ }
89138
90139 Ok ( ( ) )
91140}
0 commit comments