Skip to content

Commit c7821a4

Browse files
committed
[+] feat: add downloader crate
1 parent e63a96e commit c7821a4

File tree

17 files changed

+412
-79
lines changed

17 files changed

+412
-79
lines changed

Cargo.lock

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ pmacro = { path = "lib/pmacro" }
143143
camera = { path = "lib/camera" }
144144
bytesio = { path = "lib/bytesio" }
145145
recorder = { path = "lib/recorder" }
146+
downloader = { path = "lib/downloader" }
146147
mp4-player = { path = "lib/mp4-player" }
147148
image-effect = { path = "lib/image-effect" }
148149
video-encoder = { path = "lib/video-encoder" }

lib/background-remover/src/model.rs

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,31 @@
22
pub enum Model {
33
Modnet,
44
Rmbg14,
5-
Rmbg14Fp16,
6-
Rmbg14Quantized,
7-
U2NET,
8-
U2NETP,
95
}
106

117
impl Model {
128
pub fn all_models() -> Vec<Self> {
13-
vec![
14-
Self::Modnet,
15-
Self::Rmbg14,
16-
Self::Rmbg14Fp16,
17-
Self::Rmbg14Quantized,
18-
Self::U2NET,
19-
Self::U2NETP,
20-
]
9+
vec![Self::Modnet, Self::Rmbg14]
2110
}
2211

2312
pub fn to_input_size(&self) -> (u32, u32) {
2413
match self {
2514
Model::Modnet => (512, 512),
26-
Model::Rmbg14 | Model::Rmbg14Fp16 | Model::Rmbg14Quantized => (1024, 1024),
27-
Model::U2NET | Model::U2NETP => (320, 320),
15+
Model::Rmbg14 => (1024, 1024),
2816
}
2917
}
3018

3119
pub fn to_str(&self) -> &'static str {
3220
match self {
3321
Self::Modnet => "modnet_photographic_portrait_matting.onnx",
3422
Self::Rmbg14 => "rmbg-1.4.onnx",
35-
Self::Rmbg14Fp16 => "rmbg-1.4_fp16.onnx",
36-
Self::Rmbg14Quantized => "rmbg-1.4_quantized.onnx",
37-
Self::U2NET => "u2net.onnx",
38-
Self::U2NETP => "u2netp.onnx",
3923
}
4024
}
4125

4226
pub fn try_from(model: &str) -> Option<Self> {
4327
match model {
4428
"modnet_photographic_portrait_matting.onnx" => Some(Model::Modnet),
4529
"rmbg-1.4.onnx" => Some(Model::Rmbg14),
46-
"rmbg-1.4_fp16.onnx" => Some(Model::Rmbg14Fp16),
47-
"rmbg-1.4_quantized.onnx" => Some(Model::Rmbg14Quantized),
48-
"u2net.onnx" => Some(Model::U2NET),
49-
"u2netp.onnx" => Some(Model::U2NETP),
5030
_ => None,
5131
}
5232
}
@@ -57,16 +37,6 @@ impl Model {
5737
"https://huggingface.co/TheEeeeLin/HivisionIDPhotos_matting/resolve/034769305faf641ad94edfac654aba13be06e816/modnet_photographic_portrait_matting.onnx"
5838
}
5939
Self::Rmbg14 => "https://huggingface.co/briaai/RMBG-1.4/resolve/main/onnx/model.onnx",
60-
Self::Rmbg14Fp16 => {
61-
"https://huggingface.co/briaai/RMBG-1.4/resolve/main/onnx/model_fp16.onnx"
62-
}
63-
Self::Rmbg14Quantized => {
64-
"https://huggingface.co/briaai/RMBG-1.4/resolve/main/onnx/model_quantized.onnx"
65-
}
66-
Self::U2NET => "https://huggingface.co/AlenZeng/u2netonnxmodel/resolve/main/u2net.onnx",
67-
Self::U2NETP => {
68-
"https://huggingface.co/martintomov/comfy/resolve/1b0c3477e152d8a2dea8e4e418a6dba32de56fda/rembg/u2netp.onnx"
69-
}
7040
}
7141
}
7242
}

lib/downloader/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.mp4

lib/downloader/Cargo.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "downloader"
3+
license.workspace = true
4+
edition.workspace = true
5+
version.workspace = true
6+
readme.workspace = true
7+
authors.workspace = true
8+
keywords.workspace = true
9+
homepage.workspace = true
10+
repository.workspace = true
11+
description.workspace = true
12+
13+
[dependencies]
14+
thiserror.workspace = true
15+
futures.workspace = true
16+
reqwest = { workspace = true, features = ["stream"] }
17+
18+
[dev-dependencies]
19+
anyhow.workspace = true
20+
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use anyhow::Result;
2+
use downloader::{DownloadError, DownloadStatus, Downloader};
3+
use std::io::Write;
4+
5+
#[tokio::main]
6+
async fn main() -> Result<(), DownloadError> {
7+
let download_url =
8+
"https://freetestdata.com/wp-content/uploads/2022/02/Free_Test_Data_1MB_MP4.mp4";
9+
let save_path = "./test_video.mp4";
10+
11+
println!("Starting download from: {}", download_url);
12+
println!("Saving to: {}", save_path);
13+
println!("Press Ctrl+C to cancel...\n");
14+
15+
let downloader = Downloader::new(download_url.to_string(), save_path.to_string());
16+
17+
match downloader
18+
.start(|downloaded: u64, total: u64, progress: f32| {
19+
let percent = progress * 100.0;
20+
let mb_downloaded = downloaded as f64 / 1024.0 / 1024.0;
21+
let mb_total = total as f64 / 1024.0 / 1024.0;
22+
print!(
23+
"\rProgress: {:.2}% ({:.2} MB / {:.2} MB)",
24+
percent, mb_downloaded, mb_total
25+
);
26+
std::io::stdout().flush().unwrap();
27+
})
28+
.await
29+
{
30+
Ok(DownloadStatus::Finsished) => {
31+
println!("\n✓ Download completed successfully!");
32+
println!("File saved to: {}", save_path);
33+
}
34+
Ok(DownloadStatus::Cancelled) => {
35+
println!("\n✗ Download was cancelled!");
36+
}
37+
Ok(DownloadStatus::Downloading) => {
38+
println!("\n⏳ Download in progress...");
39+
}
40+
Err(e) => {
41+
println!("\n✗ Download failed: {}", e);
42+
return Err(e);
43+
}
44+
}
45+
46+
Ok(())
47+
}

lib/downloader/src/downloader.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use crate::{DownloadError, Result};
2+
use futures::StreamExt;
3+
use reqwest::Client;
4+
use std::{
5+
fs,
6+
io::Write,
7+
sync::{
8+
Arc,
9+
atomic::{AtomicBool, Ordering},
10+
},
11+
};
12+
13+
pub enum DownloadStatus {
14+
Finsished,
15+
Cancelled,
16+
Downloading,
17+
}
18+
19+
#[derive(Debug, Clone)]
20+
#[non_exhaustive]
21+
pub struct Downloader {
22+
url: String,
23+
save_path: String,
24+
cancel_sig: Arc<AtomicBool>,
25+
}
26+
27+
impl Downloader {
28+
pub fn new(url: String, save_path: String) -> Downloader {
29+
Downloader {
30+
url,
31+
save_path,
32+
cancel_sig: Arc::new(AtomicBool::new(false)),
33+
}
34+
}
35+
36+
pub async fn start(
37+
&self,
38+
mut progress_cb: impl FnMut(u64, u64, f32) + 'static,
39+
) -> Result<DownloadStatus> {
40+
let tmp_filepath = format!("{}.tmp", self.save_path);
41+
42+
let mut save_file =
43+
fs::File::create(&tmp_filepath).map_err(|e| DownloadError::FileCreateError {
44+
error: e,
45+
path: tmp_filepath.clone(),
46+
})?;
47+
48+
let response =
49+
Client::new()
50+
.get(&self.url)
51+
.send()
52+
.await
53+
.map_err(|e| DownloadError::RequestError {
54+
error: e,
55+
url: self.url.to_string(),
56+
})?;
57+
58+
let total_size = response
59+
.content_length()
60+
.ok_or_else(|| DownloadError::ContentLengthError)?;
61+
62+
let mut downloaded: u64 = 0;
63+
let mut stream = response.bytes_stream();
64+
65+
while let Some(chunk) = stream.next().await {
66+
if self.cancel_sig.load(Ordering::Relaxed) {
67+
return Ok(DownloadStatus::Cancelled);
68+
}
69+
70+
let chunk = chunk.map_err(|e| DownloadError::IncompleteDownload {
71+
error: e.to_string(),
72+
downloaded,
73+
total: total_size,
74+
})?;
75+
save_file.write_all(&chunk)?;
76+
77+
downloaded += chunk.len() as u64;
78+
79+
let progress = downloaded as f32 / total_size as f32;
80+
progress_cb(downloaded, total_size, progress);
81+
}
82+
83+
if total_size == downloaded {
84+
_ = fs::rename(&tmp_filepath, &self.save_path);
85+
Ok(DownloadStatus::Finsished)
86+
} else {
87+
Ok(DownloadStatus::Downloading)
88+
}
89+
}
90+
91+
pub fn cancel(&self) {
92+
self.cancel_sig.store(true, Ordering::Relaxed);
93+
}
94+
95+
pub fn cancel_sig(&self) -> Arc<AtomicBool> {
96+
self.cancel_sig.clone()
97+
}
98+
}

lib/downloader/src/lib.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
pub mod downloader;
2+
3+
pub use downloader::{DownloadStatus, Downloader};
4+
5+
pub type Result<T> = std::result::Result<T, DownloadError>;
6+
7+
#[derive(thiserror::Error, Debug)]
8+
pub enum DownloadError {
9+
#[error("HTTP request {url} failed. Error: {error}")]
10+
RequestError { error: reqwest::Error, url: String },
11+
12+
#[error("IO error: {0}")]
13+
IoError(#[from] std::io::Error),
14+
15+
#[error("Failed to get content length from response")]
16+
ContentLengthError,
17+
18+
#[error("Download operation cancelled")]
19+
Cancelled,
20+
21+
#[error("Download incomplete: {downloaded}/{total} bytes. Error: {error}")]
22+
IncompleteDownload {
23+
error: String,
24+
downloaded: u64,
25+
total: u64,
26+
},
27+
28+
#[error("Failed to create file: {path}. Error: {error}")]
29+
FileCreateError { error: std::io::Error, path: String },
30+
}

wayshot/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ native-dialog.workspace = true
5252
platform-dirs.workspace = true
5353
screen-capture.workspace = true
5454
fast_image_resize.workspace = true
55+
background-remover.workspace = true
5556
rodio = { workspace = true, features = ["playback"] }
5657

5758
[target.'cfg(target_os = "linux")'.dependencies]

0 commit comments

Comments
 (0)