chore: fix download path

This commit is contained in:
michel
2025-10-26 01:15:17 +02:00
committed by Michel de Graaf
parent f196c86328
commit 436bc0fe1a
4 changed files with 52 additions and 44 deletions
+3
View File
@@ -187,6 +187,7 @@ impl Client {
filename: String, filename: String,
username: String, username: String,
size: u64, size: u64,
download_directory: String,
) -> Result<crate::types::DownloadResult> { ) -> Result<crate::types::DownloadResult> {
use crate::types::{DownloadResult, DownloadStatus}; use crate::types::{DownloadResult, DownloadStatus};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@@ -201,6 +202,7 @@ impl Client {
filename: filename.clone(), filename: filename.clone(),
token, token,
size, size,
download_directory,
}; };
let mut context = self.context.lock().unwrap(); let mut context = self.context.lock().unwrap();
@@ -436,6 +438,7 @@ impl Client {
filename: transfer.filename, filename: transfer.filename,
token: transfer.token, token: transfer.token,
size: transfer.size, size: transfer.size,
download_directory: "~/Downloads/".to_string(),
}, },
); );
} }
+6 -6
View File
@@ -22,28 +22,28 @@ fn main() {
file.name, file.name,
file.username, file.username,
file.size, file.size,
"~/Downloads".to_string(),
) { ) {
Ok(download_result) => { Ok(download_result) => {
println!( println!(
"Download result: {:?}", "Download result: {download_result:?}"
download_result
); );
} }
Err(e) => { Err(e) => {
eprintln!("Failed to download: {}", e); eprintln!("Failed to download: {e}");
} }
} }
} else { } else {
eprint!("No results") eprint!("No results");
} }
} }
Err(e) => { Err(e) => {
eprintln!("Failed to search: {}", e); eprintln!("Failed to search: {e}");
} }
} }
} }
Err(e) => { Err(e) => {
eprintln!("Failed to login: {}", e); eprintln!("Failed to login: {e}");
} }
} }
} }
+42 -38
View File
@@ -1,11 +1,12 @@
use crate::client::ClientContext; use crate::client::ClientContext;
use crate::message::server::MessageFactory; use crate::message::server::MessageFactory;
use crate::trace; use crate::trace;
use std::env;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::{self, BufWriter, Read, Write}; use std::io::{self, BufWriter, Read, Write};
use std::net::TcpStream; use std::net::TcpStream;
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
use std::path::Path; use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Duration; use std::time::Duration;
@@ -21,6 +22,14 @@ struct FileManager;
#[allow(dead_code)] #[allow(dead_code)]
impl FileManager { impl FileManager {
fn expand_path(mut path: &str) -> String {
if path.starts_with('~') {
let home = env::var("HOME").expect("HOME not set");
format!("{}{}", home, &path[1..])
} else {
path.to_string()
}
}
fn create_download_paths( fn create_download_paths(
output_path: Option<String>, output_path: Option<String>,
username: &str, username: &str,
@@ -28,9 +37,9 @@ impl FileManager {
) -> DownloadPaths { ) -> DownloadPaths {
let final_path = match output_path { let final_path = match output_path {
Some(path) if !path.is_empty() => path, Some(path) if !path.is_empty() => path,
_ => format!("/tmp/{}_{}.mp3", username, token), _ => format!("/tmp/{username}_{token}.mp3"),
}; };
let incomplete_path = format!("{}.incomplete", final_path); let incomplete_path = format!("{final_path}.incomplete");
DownloadPaths { DownloadPaths {
final_path, final_path,
@@ -63,22 +72,31 @@ impl FileManager {
fn extract_filename_from_path(full_path: &str) -> String { fn extract_filename_from_path(full_path: &str) -> String {
// Split on both forward slashes and backslashes to handle Windows and Unix paths // Split on both forward slashes and backslashes to handle Windows and Unix paths
full_path.split(['/', '\\']).next_back().unwrap_or(full_path).to_string() full_path
.split(['/', '\\'])
.next_back()
.unwrap_or(full_path)
.to_string()
} }
fn create_download_path_from_filename( fn create_download_path_from_filename(
output_directory: Option<&str>, output_directory: Option<&str>,
remote_username: &str, remote_username: &str,
token: u32, token: u32,
filename: Option<&str> filename: Option<&str>,
) -> String { ) -> String {
let base_dir = output_directory.unwrap_or("/tmp"); let base_dir = output_directory.unwrap_or("/tmp");
let mut expanded_base_dir = FileManager::expand_path(base_dir);
if expanded_base_dir.ends_with('/') {
expanded_base_dir.pop();
}
if let Some(filename) = filename { if let Some(filename) = filename {
let clean_filename = Self::extract_filename_from_path(filename); let clean_filename = Self::extract_filename_from_path(filename);
format!("{}/{}", base_dir, clean_filename) format!("{expanded_base_dir}/{clean_filename}")
} else { } else {
format!("{}/{}_{}.mp3", base_dir, remote_username, token) format!("{expanded_base_dir}/{remote_username}_{token}")
} }
} }
} }
@@ -118,13 +136,9 @@ impl StreamProcessor {
Ok(false) Ok(false)
} }
fn process_data_chunk( fn process_data_chunk(&mut self, data: &[u8]) {
&mut self,
data: &[u8],
) -> Result<(), io::Error> {
self.buffer.extend_from_slice(data); self.buffer.extend_from_slice(data);
self.total_bytes += data.len(); self.total_bytes += data.len();
Ok(())
} }
fn should_continue(&self, expected_size: Option<usize>) -> bool { fn should_continue(&self, expected_size: Option<usize>) -> bool {
@@ -148,7 +162,7 @@ pub struct DownloadPeer {
#[allow(dead_code)] #[allow(dead_code)]
impl DownloadPeer { impl DownloadPeer {
#[allow(dead_code)] #[must_use]
pub fn new( pub fn new(
username: String, username: String,
host: String, host: String,
@@ -211,10 +225,6 @@ impl DownloadPeer {
); );
stream.write_all(&message.get_buffer())?; stream.write_all(&message.get_buffer())?;
stream.flush()?; stream.flush()?;
// stream
// .write_all(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])?;
// stream.flush()?;
//
} else { } else {
stream.write_all( stream.write_all(
&MessageFactory::build_pierce_firewall_message(self.token) &MessageFactory::build_pierce_firewall_message(self.token)
@@ -236,7 +246,7 @@ impl DownloadPeer {
self, self,
client_context: Arc<Mutex<ClientContext>>, client_context: Arc<Mutex<ClientContext>>,
mut expected_size: Option<usize>, mut expected_size: Option<usize>,
output_path: Option<String>, mut download_path: Option<String>,
) -> Result<(usize, String), io::Error> { ) -> Result<(usize, String), io::Error> {
let mut stream = self.establish_connection()?; let mut stream = self.establish_connection()?;
trace!("[download_peer:{}] connected", self.username); trace!("[download_peer:{}] connected", self.username);
@@ -294,6 +304,8 @@ impl DownloadPeer {
download.filename download.filename
); );
expected_size = Some(download.size as usize); expected_size = Some(download.size as usize);
download_path =
Some(download.download_directory.clone());
} }
None => { None => {
trace!( trace!(
@@ -304,18 +316,15 @@ impl DownloadPeer {
} }
} }
continue; // Skip this data chunk continue;
} }
processor.process_data_chunk(data)?; processor.process_data_chunk(data);
if !processor.should_continue(expected_size) { if !processor.should_continue(expected_size) {
break; break;
} }
} }
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => {
continue;
}
Err(e) => { Err(e) => {
return Err(e); return Err(e);
} }
@@ -326,8 +335,7 @@ impl DownloadPeer {
self.username self.username
); );
// Now determine the final file path using download info let output_directory = download_path.as_ref().map(|path| {
let output_directory = output_path.as_ref().map(|path| {
if Path::new(path).is_dir() { if Path::new(path).is_dir() {
path.as_str() path.as_str()
} else if let Some(parent) = Path::new(path).parent() { } else if let Some(parent) = Path::new(path).parent() {
@@ -339,35 +347,31 @@ impl DownloadPeer {
let filename = download_info.as_ref().map(|d| d.filename.as_str()); let filename = download_info.as_ref().map(|d| d.filename.as_str());
let final_path = if let Some(output_path) = &output_path { let final_path = if let Some(output_path) = &download_path {
if Path::new(output_path).is_dir() || output_path.ends_with('/') { if Path::new(output_path).is_dir() || output_path.ends_with('/') {
// output_path is a directory, append filename // output_path is a directory, append filename
FileManager::create_download_path_from_filename( FileManager::create_download_path_from_filename(
Some(output_path), Some(output_path),
&self.username, &self.username,
self.token, self.token,
filename filename,
) )
} else { } else {
// output_path is a full file path, use it directly
output_path.clone() output_path.clone()
} }
} else { } else {
// No output path provided, use default logic
FileManager::create_download_path_from_filename( FileManager::create_download_path_from_filename(
output_directory, output_directory,
&self.username, &self.username,
self.token, self.token,
filename filename,
) )
}; };
// Create directory if needed
if let Some(parent) = Path::new(&final_path).parent() { if let Some(parent) = Path::new(&final_path).parent() {
fs::create_dir_all(parent)?; fs::create_dir_all(parent)?;
} }
// Write the buffer to the final file
fs::write(&final_path, &processor.buffer)?; fs::write(&final_path, &processor.buffer)?;
trace!( trace!(
@@ -527,7 +531,7 @@ mod tests {
let download_peer = DownloadPeer::new( let download_peer = DownloadPeer::new(
"remote_user".to_string(), "remote_user".to_string(),
"127.0.0.1".to_string(), "127.0.0.1".to_string(),
port as u32, u32::from(port),
42, 42,
false, false,
"test_user".to_string(), "test_user".to_string(),
@@ -574,7 +578,7 @@ mod tests {
#[test] #[test]
fn test_file_manager_create_paths_with_empty_string() { fn test_file_manager_create_paths_with_empty_string() {
let paths = FileManager::create_download_paths( let paths = FileManager::create_download_paths(
Some("".to_string()), Some(String::new()),
"user", "user",
789, 789,
); );
@@ -705,7 +709,7 @@ mod tests {
Some("/downloads"), Some("/downloads"),
"remote_user", "remote_user",
123, 123,
Some("song.mp3") Some("song.mp3"),
); );
assert_eq!(path, "/downloads/song.mp3"); assert_eq!(path, "/downloads/song.mp3");
@@ -714,7 +718,7 @@ mod tests {
Some("/downloads"), Some("/downloads"),
"remote_user", "remote_user",
123, 123,
Some("/remote/path/to/song.mp3") Some("/remote/path/to/song.mp3"),
); );
assert_eq!(path, "/downloads/song.mp3"); assert_eq!(path, "/downloads/song.mp3");
@@ -723,7 +727,7 @@ mod tests {
Some("/downloads"), Some("/downloads"),
"remote_user", "remote_user",
123, 123,
None None,
); );
assert_eq!(path, "/downloads/remote_user_123.mp3"); assert_eq!(path, "/downloads/remote_user_123.mp3");
@@ -732,7 +736,7 @@ mod tests {
None, None,
"remote_user", "remote_user",
123, 123,
Some("song.mp3") Some("song.mp3"),
); );
assert_eq!(path, "/tmp/song.mp3"); assert_eq!(path, "/tmp/song.mp3");
} }
+1
View File
@@ -87,6 +87,7 @@ pub struct Download {
pub filename: String, pub filename: String,
pub token: u32, pub token: u32,
pub size: u64, pub size: u64,
pub download_directory: String,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]