diff --git a/Cargo.lock b/Cargo.lock index ad25017..c70f2ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1084,7 +1084,7 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "streamshare" -version = "3.0.0" +version = "4.0.0" dependencies = [ "futures", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index c3de68f..cccd200 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "streamshare" -version = "3.0.0" +version = "4.0.0" edition = "2021" description = "Upload to streamshare library" license = "MIT" diff --git a/README.md b/README.md index f47b132..9c44076 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ Upload files to [streamshare](https://streamshare.wireway.ch) Upload: ```rust +let client = StreamShare::default(); + let callback = |uploaded_bytes, total_bytes| { println!( "Uploaded {}b of {}b", @@ -15,7 +17,7 @@ let callback = |uploaded_bytes, total_bytes| { ); } -match upload(&file_path, callback).await { +match client.upload(&file_path, callback).await { Ok((file_identifier, _deletion_token)) => { let download_url = format!( "https://streamshare.wireway.ch/download/{}", @@ -32,7 +34,9 @@ match upload(&file_path, callback).await { Delete: ```rust -match streamshare::delete(file_identifier, deletion_token).await { +let client = StreamShare::default(); + +match client.delete(file_identifier, deletion_token).await { Ok(_) => println!("File deleted successfully"), Err(e) => eprintln!("Error deleting file: {}", e), } diff --git a/src/lib.rs b/src/lib.rs index eb0746f..2240940 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,97 +17,124 @@ struct CreateResponse { deletion_token: String, } -pub async fn upload(file_path: &str, mut callback: F) -> Result<(String, String), Box> -where - F: FnMut(u64, u64) -{ - let path = Path::new(file_path); - let metadata = fs::metadata(path).await?; - if !metadata.is_file() { - return Err("Selected item is not a file".into()); +pub struct StreamShare { + server_url: String, + chunk_size: usize, + client: Client, +} + +impl StreamShare { + pub fn new(server_url: String, chunk_size: usize) -> Self { + Self { + server_url: server_url, + chunk_size: chunk_size, + client: Client::new(), + } } - let file_size = metadata.len(); - let file_name = path - .file_name() - .and_then(|s| s.to_str()) - .unwrap_or("unknown"); - - let client = Client::new(); - let create_url = "https://streamshare.wireway.ch/api/create"; - - let res = client - .post(create_url) - .json(&serde_json::json!({ "name": file_name })) - .send() - .await?; - - if !res.status().is_success() { - return Err(format!("Failed to create upload: {}", res.status()).into()); - } - - let create_response: CreateResponse = res.json().await?; - let ws_url = format!( - "wss://streamshare.wireway.ch/api/upload/{}", - create_response.file_identifier - ); - let (mut ws_stream, _) = connect_async(ws_url).await?; - - let mut file = File::open(path).await?; - const CHUNK_SIZE: usize = 1024 * 1024; - let mut buffer = vec![0u8; CHUNK_SIZE]; - let mut uploaded: u64 = 0; - - loop { - let n = file.read(&mut buffer).await?; - - if n == 0 { - break; + pub async fn upload( + &self, + file_path: &str, + mut callback: F, + ) -> Result<(String, String), Box> + where + F: FnMut(u64, u64), + { + let path = Path::new(file_path); + let metadata = fs::metadata(path).await?; + if !metadata.is_file() { + return Err(format!("Selected item is not a file: {}", file_path).into()); } - let chunk = &buffer[..n]; - ws_stream.send(Message::Binary(chunk.to_vec())).await?; - uploaded += n as u64; + let file_size = metadata.len(); + let file_name = path + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or("unknown"); - callback(uploaded, file_size); + let create_url = format!("https://{}/api/create", self.server_url); - match ws_stream.next().await { - Some(Ok(Message::Text(text))) if text == "ACK" => (), - Some(Ok(msg)) => { - return Err(format!("Unexpected message: {:?}", msg).into()); + let res = self + .client + .post(&create_url) + .json(&serde_json::json!({ "name": file_name })) + .send() + .await?; + + if !res.status().is_success() { + return Err(format!("Failed to create upload: {}", res.status()).into()); + } + + let create_response: CreateResponse = res.json().await?; + let ws_url = format!( + "wss://{}/api/upload/{}", + self.server_url, create_response.file_identifier + ); + let (mut ws_stream, _) = connect_async(ws_url).await?; + + let mut file = File::open(path).await?; + let mut buffer = vec![0u8; self.chunk_size]; + let mut uploaded: u64 = 0; + + loop { + let n = file.read(&mut buffer).await?; + if n == 0 { + break; } - Some(Err(e)) => return Err(format!("WebSocket error: {}", e).into()), - None => return Err("WebSocket closed unexpectedly".into()), + + let chunk = &buffer[..n]; + ws_stream.send(Message::Binary(chunk.to_vec())).await?; + uploaded += n as u64; + callback(uploaded, file_size); + + match ws_stream.next().await { + Some(Ok(Message::Text(text))) if text == "ACK" => (), + Some(Ok(msg)) => { + return Err(format!("Unexpected message: {:?}", msg).into()); + } + Some(Err(e)) => return Err(format!("WebSocket error: {}", e).into()), + None => return Err("WebSocket closed unexpectedly".into()), + } + } + + ws_stream + .close(Some(tungstenite::protocol::CloseFrame { + code: tungstenite::protocol::frame::coding::CloseCode::Normal, + reason: "FILE_UPLOAD_DONE".into(), + })) + .await?; + + Ok(( + create_response.file_identifier, + create_response.deletion_token, + )) + } + + pub async fn delete( + &self, + file_identifier: &str, + deletion_token: &str, + ) -> Result<(), Box> { + let delete_url = format!( + "https://{}/api/delete/{}/{}", + self.server_url, file_identifier, deletion_token + ); + + let res = self.client.delete(&delete_url).send().await?; + if res.status().is_success() { + Ok(()) + } else { + Err(format!("Failed to delete file: {}", res.status()).into()) } } - - ws_stream - .close(Some(tungstenite::protocol::CloseFrame { - code: tungstenite::protocol::frame::coding::CloseCode::Normal, - reason: "FILE_UPLOAD_DONE".into(), - })) - .await?; - - Ok(( - create_response.file_identifier, - create_response.deletion_token, - )) } -pub async fn delete( - file_identifier: &str, - deletion_token: &str, -) -> Result<(), Box> { - let client = Client::new(); - let delete_url = format!( - "https://streamshare.wireway.ch/api/delete/{}/{}", - file_identifier, deletion_token - ); - - let res = client.delete(&delete_url).send().await?; - if res.status().is_success() { - Ok(()) - } else { - Err(format!("Failed to delete file: {}", res.status()).into()) +impl Default for StreamShare { + fn default() -> Self { + Self { + server_url: "streamshare.wireway.ch".to_string(), + chunk_size: 1024 * 1024, + client: Client::new(), + } } }