chore: fix download path
This commit is contained in:
@@ -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
@@ -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
@@ -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");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
Reference in New Issue
Block a user