added progress indicator

This commit is contained in:
Waradu 2024-10-20 10:04:09 +02:00
parent 9dcb098754
commit 11264e4be5
No known key found for this signature in database
GPG key ID: F85AAC8BA8B8DAAD
4 changed files with 54 additions and 11 deletions

2
Cargo.lock generated
View file

@ -1084,7 +1084,7 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]] [[package]]
name = "streamshare" name = "streamshare"
version = "1.1.1" version = "2.0.0"
dependencies = [ dependencies = [
"futures", "futures",
"reqwest", "reqwest",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "streamshare" name = "streamshare"
version = "1.1.1" version = "2.0.0"
edition = "2021" edition = "2021"
description = "Upload to streamshare library" description = "Upload to streamshare library"
license = "MIT" license = "MIT"

View file

@ -6,7 +6,8 @@ Upload files to [streamshare](https://streamshare.wireway.ch)
Upload: Upload:
```rust ```rust
match upload(&file_path).await { let show_progress = true;
match upload(&file_path, show_progress).await {
Ok((file_identifier, _deletion_token)) => { Ok((file_identifier, _deletion_token)) => {
let download_url = format!( let download_url = format!(
"https://streamshare.wireway.ch/download/{}", "https://streamshare.wireway.ch/download/{}",

View file

@ -1,7 +1,7 @@
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use reqwest::Client; use reqwest::Client;
use serde::Deserialize; use serde::Deserialize;
use std::path::Path; use std::{io::Write, path::Path, time::Instant};
use tokio::fs; use tokio::fs;
use tokio::fs::File; use tokio::fs::File;
use tokio::io::AsyncReadExt; use tokio::io::AsyncReadExt;
@ -17,13 +17,14 @@ struct CreateResponse {
deletion_token: String, deletion_token: String,
} }
pub async fn upload(file_path: &str) -> Result<(String, String), Box<dyn std::error::Error>> { pub async fn upload(file_path: &str, show_progress: bool) -> Result<(String, String), Box<dyn std::error::Error>> {
let path = Path::new(file_path); let path = Path::new(file_path);
let metadata = fs::metadata(path).await?; let metadata = fs::metadata(path).await?;
if !metadata.is_file() { if !metadata.is_file() {
return Err("Selected item is not a file".into()); return Err("Selected item is not a file".into());
} }
let file_size = metadata.len();
let file_name = path let file_name = path
.file_name() .file_name()
.and_then(|s| s.to_str()) .and_then(|s| s.to_str())
@ -43,7 +44,6 @@ pub async fn upload(file_path: &str) -> Result<(String, String), Box<dyn std::er
} }
let create_response: CreateResponse = res.json().await?; let create_response: CreateResponse = res.json().await?;
let ws_url = format!( let ws_url = format!(
"wss://streamshare.wireway.ch/api/upload/{}", "wss://streamshare.wireway.ch/api/upload/{}",
create_response.file_identifier create_response.file_identifier
@ -51,10 +51,10 @@ pub async fn upload(file_path: &str) -> Result<(String, String), Box<dyn std::er
let (mut ws_stream, _) = connect_async(ws_url).await?; let (mut ws_stream, _) = connect_async(ws_url).await?;
let mut file = File::open(path).await?; let mut file = File::open(path).await?;
const CHUNK_SIZE: usize = 512 * 1024;
const CHUNK_SIZE: usize = 64 * 1024;
let mut buffer = vec![0u8; CHUNK_SIZE]; let mut buffer = vec![0u8; CHUNK_SIZE];
let mut uploaded: u64 = 0;
let start_time = Instant::now();
loop { loop {
let n = file.read(&mut buffer).await?; let n = file.read(&mut buffer).await?;
@ -64,8 +64,22 @@ pub async fn upload(file_path: &str) -> Result<(String, String), Box<dyn std::er
} }
let chunk = &buffer[..n]; let chunk = &buffer[..n];
ws_stream.send(Message::Binary(chunk.to_vec())).await?; ws_stream.send(Message::Binary(chunk.to_vec())).await?;
uploaded += n as u64;
if show_progress {
let percentage = (uploaded as f64 / file_size as f64) * 100.0;
let uploaded_mb = bytes_to_human_readable(uploaded);
let total_mb = bytes_to_human_readable(file_size);
let elapsed_secs = start_time.elapsed().as_secs_f64();
let speed = bytes_to_human_readable((uploaded as f64 / elapsed_secs) as u64);
print!(
"\r\x1b[2K{:.2}% {}/{} ({}/s)",
percentage, uploaded_mb, total_mb, speed
);
std::io::stdout().flush().unwrap();
}
match ws_stream.next().await { match ws_stream.next().await {
Some(Ok(Message::Text(text))) if text == "ACK" => (), Some(Ok(Message::Text(text))) if text == "ACK" => (),
@ -77,6 +91,15 @@ pub async fn upload(file_path: &str) -> Result<(String, String), Box<dyn std::er
} }
} }
if show_progress {
println!(
"\r\x1b[2K100.00% {}/{} (Upload complete)",
bytes_to_human_readable(file_size),
bytes_to_human_readable(file_size)
);
println!();
}
ws_stream ws_stream
.close(Some(tungstenite::protocol::CloseFrame { .close(Some(tungstenite::protocol::CloseFrame {
code: tungstenite::protocol::frame::coding::CloseCode::Normal, code: tungstenite::protocol::frame::coding::CloseCode::Normal,
@ -90,7 +113,10 @@ pub async fn upload(file_path: &str) -> Result<(String, String), Box<dyn std::er
)) ))
} }
pub async fn delete(file_identifier: &str, deletion_token: &str) -> Result<(), Box<dyn std::error::Error>> { pub async fn delete(
file_identifier: &str,
deletion_token: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new(); let client = Client::new();
let delete_url = format!( let delete_url = format!(
"https://streamshare.wireway.ch/api/delete/{}/{}", "https://streamshare.wireway.ch/api/delete/{}/{}",
@ -104,3 +130,19 @@ pub async fn delete(file_identifier: &str, deletion_token: &str) -> Result<(), B
Err(format!("Failed to delete file: {}", res.status()).into()) Err(format!("Failed to delete file: {}", res.status()).into())
} }
} }
fn bytes_to_human_readable(bytes: u64) -> String {
const KB: f64 = 1024.0;
const MB: f64 = KB * 1024.0;
const GB: f64 = MB * 1024.0;
if bytes as f64 >= GB {
format!("{:.2}gb", bytes as f64 / GB)
} else if bytes as f64 >= MB {
format!("{:.2}mb", bytes as f64 / MB)
} else if bytes as f64 >= KB {
format!("{:.2}kb", bytes as f64 / KB)
} else {
format!("{}b", bytes)
}
}