This commit is contained in:
Igor Katson 2021-07-13 15:11:08 +01:00
parent 9e3e3a27ff
commit 48f4c0a8b7
2 changed files with 24 additions and 18 deletions

View file

@ -1,5 +1,6 @@
[package] [package]
name = "rqbit" name = "rqbit"
description = "A bittorent client"
version = "0.1.0" version = "0.1.0"
authors = ["Igor Katson <igor.katson@gmail.com>"] authors = ["Igor Katson <igor.katson@gmail.com>"]
edition = "2018" edition = "2018"

View file

@ -65,12 +65,12 @@ impl FromStr for ParsedDuration {
} }
#[derive(Clap)] #[derive(Clap)]
#[clap(version = "1.0", author = "Igor Katson <igor.katson@gmail.com>")] #[clap(version, author, about)]
struct Opts { struct Opts {
/// The filename or URL of the torrent. If URL, http/https/magnet are supported. /// The filename or URL of the torrent. If URL, http/https/magnet are supported.
torrent_path: String, torrent_path: String,
/// The filename of the .torrent file. /// The output folder to write to. If not exists, it will be created.
output_folder: String, output_folder: String,
/// If set, only the file whose filename matching this regex will /// If set, only the file whose filename matching this regex will
@ -90,11 +90,11 @@ struct Opts {
#[clap(arg_enum, short = 'v')] #[clap(arg_enum, short = 'v')]
log_level: Option<LogLevel>, log_level: Option<LogLevel>,
/// The interval in seconds to poll trackers. /// The interval to poll trackers, e.g. 30s.
/// Trackers send the refresh interval when we connect to them. Often this is /// Trackers send the refresh interval when we connect to them. Often this is
/// pretty big, e.g. 30 minutes. This can force a certain value. /// pretty big, e.g. 30 minutes. This can force a certain value.
#[clap(short = 'i', long = "tracker-refresh-interval")] #[clap(short = 'i', long = "tracker-refresh-interval")]
force_tracker_interval: Option<u64>, force_tracker_interval: Option<ParsedDuration>,
/// The listen address for HTTP API /// The listen address for HTTP API
#[clap(long = "http-api-listen-addr", default_value = "127.0.0.1:3030")] #[clap(long = "http-api-listen-addr", default_value = "127.0.0.1:3030")]
@ -198,15 +198,29 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
..Default::default() ..Default::default()
}; };
// Magnet links are different in that we first need to discover the metadata.
if opts.torrent_path.starts_with("magnet:") { if opts.torrent_path.starts_with("magnet:") {
let Magnet { let Magnet {
info_hash, info_hash,
trackers, trackers,
} = Magnet::parse(&opts.torrent_path).context("provided path is not a valid magnet URL")?; } = Magnet::parse(&opts.torrent_path).context("provided path is not a valid magnet URL")?;
let dht_rx = dht let dht_rx = dht
.ok_or_else(|| anyhow::anyhow!("magnet links without DHT are not supported"))? .ok_or_else(|| anyhow::anyhow!("magnet links without DHT are not supported"))?
.get_peers(info_hash) .get_peers(info_hash)
.await; .await;
let trackers = trackers
.into_iter()
.filter_map(|url| match reqwest::Url::parse(&url) {
Ok(url) => Some(url),
Err(e) => {
warn!("error parsing tracker {} as url: {}", url, e);
None
}
})
.collect();
let (info, dht_rx, initial_peers) = let (info, dht_rx, initial_peers) =
match read_metainfo_from_peer_receiver(peer_id, info_hash, dht_rx, Some(peer_opts)) match read_metainfo_from_peer_receiver(peer_id, info_hash, dht_rx, Some(peer_opts))
.await .await
@ -216,23 +230,14 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
anyhow::bail!("DHT died, no way to discover torrent metainfo") anyhow::bail!("DHT died, no way to discover torrent metainfo")
} }
}; };
main_info( main_torrent_info(
opts, opts,
info_hash, info_hash,
info, info,
peer_id, peer_id,
Some(dht_rx), Some(dht_rx),
initial_peers.into_iter().collect(), initial_peers.into_iter().collect(),
trackers trackers,
.into_iter()
.filter_map(|url| match reqwest::Url::parse(&url) {
Ok(url) => Some(url),
Err(e) => {
warn!("error parsing tracker {} as url: {}", url, e);
None
}
})
.collect(),
spawner, spawner,
) )
.await .await
@ -267,7 +272,7 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
main_info( main_torrent_info(
opts, opts,
torrent.info_hash, torrent.info_hash,
torrent.info, torrent.info,
@ -282,7 +287,7 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
async fn main_info( async fn main_torrent_info(
opts: Opts, opts: Opts,
info_hash: Id20, info_hash: Id20,
info: TorrentMetaV1Info<ByteString>, info: TorrentMetaV1Info<ByteString>,
@ -313,7 +318,7 @@ async fn main_info(
builder.only_files(only_files); builder.only_files(only_files);
} }
if let Some(interval) = opts.force_tracker_interval { if let Some(interval) = opts.force_tracker_interval {
builder.force_tracker_interval(Duration::from_secs(interval)); builder.force_tracker_interval(interval.0);
} }
if let Some(t) = opts.peer_connect_timeout { if let Some(t) = opts.peer_connect_timeout {
builder.peer_connect_timeout(t.0); builder.peer_connect_timeout(t.0);