This commit is contained in:
Waradu 2024-10-19 22:20:44 +02:00
commit 67788708fd
No known key found for this signature in database
GPG key ID: F85AAC8BA8B8DAAD
5 changed files with 1746 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

1622
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

18
Cargo.toml Normal file
View file

@ -0,0 +1,18 @@
[package]
name = "streamshare"
version = "1.0.0"
edition = "2021"
description = "Upload to streamshare library"
license = "MIT"
homepage = "https://waradu.dev"
repository = "https://github.com/Waradu/streamshare"
readme = "README.md"
authors = ["Waradu"]
[dependencies]
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.12.8", features = ["json", "rustls-tls"] }
tokio-tungstenite = { version = "0.24.0", features = ["rustls-tls-webpki-roots"] }
futures = "0.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

20
README.md Normal file
View file

@ -0,0 +1,20 @@
# Streamshare
Upload files to [streamshare](https://streamshare.wireway.ch)
#### Example:
```rust
match upload(&file_path).await {
Ok((file_identifier, _deletion_token)) => {
let download_url = format!(
"https://streamshare.wireway.ch/download/{}",
file_identifier
);
println!("File uploaded successfully");
println!("Download URL: {}", download_url);
}
Err(e) => eprintln!("Error: {}", e),
}
```

85
src/lib.rs Normal file
View file

@ -0,0 +1,85 @@
use tokio::fs;
use tokio::io::AsyncReadExt;
use std::path::Path;
use reqwest::Client;
use serde::Deserialize;
use tokio::fs::File;
use tokio_tungstenite::{connect_async, tungstenite::{self, Message}};
use futures::{SinkExt, StreamExt};
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct CreateResponse {
file_identifier: String,
deletion_token: String,
}
pub async fn upload(file_path: &str) -> Result<(String, String), Box<dyn std::error::Error>> {
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());
}
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 = 64 * 1024;
let mut buffer = vec![0u8; CHUNK_SIZE];
loop {
let n = file.read(&mut buffer).await?;
if n == 0 {
break;
}
let chunk = &buffer[..n];
ws_stream.send(Message::Binary(chunk.to_vec())).await?;
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))
}