diff --git a/README.md b/README.md new file mode 100644 index 0000000..68b211d --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# void-cat-rs + +Image hosting service + +## Features +- NIP-96 Support +- Blossom Support +- Image compression to WebP (FFMPEG) + +## Planned +- Torrent seed V2 \ No newline at end of file diff --git a/src/filesystem.rs b/src/filesystem.rs index 31a95af..614b72a 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -44,32 +44,48 @@ impl FileStore { } /// Store a new file - pub async fn put(&self, mut stream: TStream, mime_type: &str) -> Result + pub async fn put(&self, stream: TStream, mime_type: &str, compress: bool) -> Result + where + TStream: AsyncRead + Unpin, + { + let result = self.store_compress_file(stream, mime_type, compress).await?; + let dst_path = self.map_path(&result.sha256); + fs::create_dir_all(dst_path.parent().unwrap())?; + if let Err(e) = fs::copy(&result.path, &dst_path) { + fs::remove_file(&result.path)?; + Err(Error::from(e)) + } else { + fs::remove_file(result.path)?; + Ok(FileSystemResult { + path: dst_path, + ..result + }) + } + } + + async fn store_compress_file(&self, mut stream: TStream, mime_type: &str, compress: bool) -> Result where TStream: AsyncRead + Unpin, { let random_id = uuid::Uuid::new_v4(); + let tmp_path = FileStore::map_temp(random_id); + let mut file = File::options() + .create(true) + .write(true) + .read(true) + .open(tmp_path.clone()) + .await?; + tokio::io::copy(&mut stream, &mut file).await?; - let mut mime_type = mime_type.to_string(); - let (n, hash, tmp_path, width, height, blur_hash) = { - let mut tmp_path = FileStore::map_temp(random_id); - let mut file = File::options() - .create(true) - .write(true) - .read(true) - .open(tmp_path.clone()) - .await?; - tokio::io::copy(&mut stream, &mut file).await?; - - info!("File saved to temp path: {}", tmp_path.to_str().unwrap()); + info!("File saved to temp path: {}", tmp_path.to_str().unwrap()); + if compress { let start = SystemTime::now(); let proc_result = { let mut p_lock = self.processor.lock().expect("asd"); p_lock.process_file(tmp_path.clone(), &mime_type)? }; if let FileProcessorResult::NewFile(new_temp) = proc_result { - mime_type = new_temp.mime_type; let old_size = tmp_path.metadata()?.len(); let new_size = new_temp.result.metadata()?.len(); info!("Compressed media: ratio={:.2}x, old_size={:.3}kb, new_size={:.3}kb, duration={:.2}ms", @@ -89,30 +105,28 @@ impl FileStore { .await?; let n = file.metadata().await?.len(); let hash = FileStore::hash_file(&mut file).await?; - (n, hash, new_temp.result, Some(new_temp.width), Some(new_temp.height), Some(new_temp.blur_hash)) - } else { - let n = file.metadata().await?.len(); - let hash = FileStore::hash_file(&mut file).await?; - (n, hash, tmp_path, None, None, None) + return Ok(FileSystemResult { + size: n, + sha256: hash, + path: new_temp.result, + width: Some(new_temp.width), + height: Some(new_temp.height), + blur_hash: Some(new_temp.blur_hash), + mime_type: new_temp.mime_type, + }); } - }; - let dst_path = self.map_path(&hash); - fs::create_dir_all(dst_path.parent().unwrap())?; - if let Err(e) = fs::copy(&tmp_path, &dst_path) { - fs::remove_file(&tmp_path)?; - Err(Error::from(e)) - } else { - fs::remove_file(tmp_path)?; - Ok(FileSystemResult { - size: n, - sha256: hash, - path: dst_path, - mime_type, - width, - height, - blur_hash, - }) } + let n = file.metadata().await?.len(); + let hash = FileStore::hash_file(&mut file).await?; + Ok(FileSystemResult { + path: tmp_path, + sha256: hash, + size: n, + mime_type: mime_type.to_string(), + width: None, + height: None, + blur_hash: None, + }) } async fn hash_file(file: &mut File) -> Result, Error> { diff --git a/src/routes/blossom.rs b/src/routes/blossom.rs index 9abf1c8..3e665b4 100644 --- a/src/routes/blossom.rs +++ b/src/routes/blossom.rs @@ -1,5 +1,3 @@ -use std::sync::{Mutex, RwLock}; - use chrono::Utc; use log::error; use nostr::prelude::hex; @@ -108,7 +106,7 @@ async fn upload( .unwrap_or("application/octet-stream".to_string()); match fs - .put(data.open(ByteUnit::from(settings.max_upload_bytes)), &mime_type) + .put(data.open(ByteUnit::from(settings.max_upload_bytes)), &mime_type, false) .await { Ok(blob) => { diff --git a/src/routes/nip96.rs b/src/routes/nip96.rs index ee3aae2..293b270 100644 --- a/src/routes/nip96.rs +++ b/src/routes/nip96.rs @@ -1,9 +1,7 @@ use std::collections::HashMap; -use std::sync::Mutex; use chrono::Utc; use rocket::{FromForm, Responder, Route, routes, State}; -use rocket::data::ByteUnit; use rocket::form::Form; use rocket::fs::TempFile; use rocket::serde::json::Json; @@ -161,7 +159,7 @@ async fn upload( let mime_type = form.media_type .unwrap_or("application/octet-stream"); match fs - .put(file, mime_type) + .put(file, mime_type, true) .await { Ok(blob) => {