Skip to content

Instantly share code, notes, and snippets.

@RandyMcMillan
Forked from rust-play/playground.rs
Last active May 31, 2025 20:53
Show Gist options
  • Save RandyMcMillan/d46b1117db6c8f9d580b00ab34b76878 to your computer and use it in GitHub Desktop.
Save RandyMcMillan/d46b1117db6c8f9d580b00ab34b76878 to your computer and use it in GitHub Desktop.
archive.rs
use std::{
io::{self, Write},
path::Path,
};
use tokio::io::AsyncWriteExt; // For async file writing
const DEST_DIR: &str = "torrents";
#[derive(Debug, serde::Deserialize)]
struct Torrent {
url: String,
display_name: String,
torrent_size: f64,
}
#[tokio::main] // Marks the main function as an asynchronous entry point
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut tb_input = String::new();
print!(
"Please enter the number of terabytes worth of torrents\nwould you like to download (empty for no limit): "
);
io::stdout().flush()?;
io::stdin().read_line(&mut tb_input)?;
let tb_input = tb_input.trim();
let tb: Option<f64> = if tb_input.is_empty() {
None
} else {
match tb_input.parse::<f64>() {
Ok(val) => Some((val * 100.0).round() / 100.0), // Round to 2 decimal places
Err(_) => {
eprintln!("Invalid input for terabytes. Exiting.");
std::process::exit(1);
}
}
};
let mut confirm_download = String::new();
println!("\nConfirm download with Y / N: ");
io::stdout().flush()?;
io::stdin().read_line(&mut confirm_download)?;
if confirm_download.trim().to_uppercase() != "Y" {
println!("Download cancelled.");
return Ok(());
}
println!("Downloading torrent list...");
let url = if let Some(t) = tb {
format!(
"https://annas-archive.org/dyn/generate_torrents?max_tb={}&format=json",
t
)
} else {
"https://annas-archive.org/dyn/generate_torrents?format=json".to_string()
};
println!("{}", url);
// Make an asynchronous HTTP GET request
let client = reqwest::Client::new();
let response = client.get(&url).send().await?.text().await?;
let torrents: Vec<Torrent> = serde_json::from_str(&response)?;
for torrent in &torrents {
println!("{:?}", torrent);
}
let length = torrents.len();
let mut current = 1;
let dest_path = Path::new(DEST_DIR);
if !dest_path.exists() {
println!("Destination directory not found: creating now.");
tokio::fs::create_dir(dest_path).await?;
} else {
println!("Destination directory found! Proceeding.");
}
println!("Beginning to download torrents.");
for i in torrents {
let file_path = dest_path.join(&i.display_name);
// Asynchronously download the torrent file content
let torrent_file_response = client.get(&i.url).send().await?.bytes().await?;
// Asynchronously write the bytes to the file
let mut file = tokio::fs::File::create(&file_path).await?;
file.write_all(&torrent_file_response).await?;
println!(
"{}/{}: {} - {}kb ",
current,
length,
i.display_name,
(i.torrent_size / 1024.0).round() as u64
);
current += 1;
}
println!("Torrents downloaded. Use a BitTorrent client to join the network and start seeding.");
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment