diff --git a/Cargo.lock b/Cargo.lock index dc300fc..723eea5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4436,6 +4436,7 @@ dependencies = [ "tokio-util", "tracing", "tracing-subscriber", + "url", ] [[package]] diff --git a/crates/librqbit/src/session.rs b/crates/librqbit/src/session.rs index 951e025..3fafc2d 100644 --- a/crates/librqbit/src/session.rs +++ b/crates/librqbit/src/session.rs @@ -422,6 +422,9 @@ pub struct SessionOptions { pub blocklist_url: Option, + // The list of tracker URLs to always use for each torrent. + pub trackers: HashSet, + #[cfg(feature = "disable-upload")] pub disable_upload: bool, } diff --git a/crates/rqbit/Cargo.toml b/crates/rqbit/Cargo.toml index e592b10..b56a351 100644 --- a/crates/rqbit/Cargo.toml +++ b/crates/rqbit/Cargo.toml @@ -52,6 +52,7 @@ libc = "0.2.158" signal-hook = "0.3.17" tokio-util = "0.7.11" gethostname = "0.5.0" +url = "2" [dev-dependencies] futures = { version = "0.3" } diff --git a/crates/rqbit/src/main.rs b/crates/rqbit/src/main.rs index 23ada49..01cb2ac 100644 --- a/crates/rqbit/src/main.rs +++ b/crates/rqbit/src/main.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashSet, io, net::SocketAddr, num::NonZeroU32, @@ -232,6 +233,10 @@ struct Opts { /// Downloads a p2p blocklist from this url and blocks peers from it #[arg(long, env = "RQBIT_BLOCKLIST_URL")] blocklist_url: Option, + + /// The filename with tracker URLs to always use for each torrent. + #[arg(long, env = "RQBIT_TRACKERS_FILENAME")] + trackers_filename: Option, } #[derive(Parser)] @@ -431,6 +436,22 @@ fn main() -> anyhow::Result<()> { } } +async fn parse_trackers_file(filename: &str) -> anyhow::Result> { + let content = tokio::fs::read_to_string(filename) + .await + .with_context(|| format!("error opening {filename}"))?; + Ok(content + .lines() + .filter_map(|s| { + let s = s.trim(); + if s.is_empty() { + return None; + } + url::Url::parse(s).ok() + }) + .collect()) +} + async fn async_main(opts: Opts, cancel: CancellationToken) -> anyhow::Result<()> { let log_config = init_logging(InitLoggingOptions { default_rust_log_value: Some(match opts.log_level.unwrap_or(LogLevel::Info) { @@ -449,6 +470,12 @@ async fn async_main(opts: Opts, cancel: CancellationToken) -> anyhow::Result<()> Err(e) => warn!("failed increasing open file limit: {:#}", e), }; + let trackers = if let Some(f) = opts.trackers_filename { + parse_trackers_file(&f).await? + } else { + Default::default() + }; + let mut sopts = SessionOptions { disable_dht: opts.disable_dht, disable_dht_persistence: opts.disable_dht_persistence, @@ -499,6 +526,7 @@ async fn async_main(opts: Opts, cancel: CancellationToken) -> anyhow::Result<()> download_bps: opts.ratelimit_download_bps, }, blocklist_url: opts.blocklist_url, + trackers, }; let http_api_basic_auth = if let Ok(up) = std::env::var("RQBIT_HTTP_BASIC_AUTH_USERPASS") {