forked from casey/intermodal
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
It is now possible to create a torrent file from a magnet link. The search is performed with no concurrency, and so will take a while to complete. type: added fixes: casey#255
- Loading branch information
1 parent
5935178
commit 94a9113
Showing
7 changed files
with
210 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
use crate::common::*; | ||
|
||
const URI_HELP: &str = "Generate a torrent file from a magnet URI"; | ||
|
||
const INPUT_FLAG: &str = "input-flag"; | ||
const INPUT_POSITIONAL: &str = "<INPUT>"; | ||
const INPUT_HELP: &str = "The magnet URI."; | ||
|
||
#[derive(StructOpt)] | ||
#[structopt( | ||
help_message(consts::HELP_MESSAGE), | ||
version_message(consts::VERSION_MESSAGE), | ||
about(URI_HELP) | ||
)] | ||
pub(crate) struct FromLink { | ||
#[structopt( | ||
name = INPUT_FLAG, | ||
long = "input", | ||
short = "i", | ||
value_name = "INPUT", | ||
empty_values = false, | ||
help = INPUT_HELP, | ||
)] | ||
input_flag: Option<MagnetLink>, | ||
#[structopt( | ||
name = INPUT_POSITIONAL, | ||
value_name = "INPUT", | ||
empty_values = false, | ||
required_unless = INPUT_FLAG, | ||
conflicts_with = INPUT_FLAG, | ||
help = INPUT_HELP, | ||
)] | ||
input_positional: Option<MagnetLink>, | ||
#[structopt( | ||
long = "output", | ||
short = "o", | ||
value_name = "TARGET", | ||
empty_values(false), | ||
required_if(INPUT_FLAG, "-"), | ||
required_if(INPUT_POSITIONAL, "-"), | ||
help = "Save `.torrent` file to `TARGET`; if omitted, the parameter is set to `./${INFOHASH}.torrent`." | ||
)] | ||
output: Option<PathBuf>, | ||
} | ||
|
||
impl FromLink { | ||
pub(crate) fn run(self, env: &mut Env, _options: &Options) -> Result<()> { | ||
let link = xor_args( | ||
"input_flag", | ||
&self.input_flag, | ||
"input_positional", | ||
&self.input_positional, | ||
)?; | ||
|
||
let infohash = link.infohash; | ||
let mut info: Option<Info> = None; | ||
|
||
'trackers_loop: for announce_url in &link.trackers { | ||
errln!(env, "trying `{}`", announce_url)?; | ||
|
||
let client = match tracker::Client::from_url(announce_url) { | ||
Err(err) => { | ||
errln!(env, "Couldn't connect to tracker: {}", err)?; | ||
continue; | ||
} | ||
Ok(client) => client, | ||
}; | ||
|
||
let peer_list = match client.announce_exchange(link.infohash) { | ||
Ok(peer_list) => peer_list, | ||
Err(err) => { | ||
errln!(env, "Couldn't connect to tracker: {}", err)?; | ||
continue; | ||
} | ||
}; | ||
|
||
for peer in peer_list { | ||
let mut peer = match peer::Client::connect(&peer, link.infohash) { | ||
Ok(peer) => peer, | ||
Err(err) => { | ||
errln!(env, "Couldn't connect to peer: {}", err)?; | ||
continue; | ||
} | ||
}; | ||
|
||
match peer.fetch_info_dict() { | ||
Ok(infodict) => { | ||
errln!(env, "Received and verified info")?; | ||
info.replace(infodict); | ||
break 'trackers_loop; | ||
} | ||
Err(err) => { | ||
errln!(env, "Couldn't fetch info: {}", err)?; | ||
continue; | ||
} | ||
}; | ||
} | ||
} | ||
|
||
let metainfo = match info { | ||
Some(info) => Metainfo { | ||
announce: None, | ||
announce_list: Some(vec![link.trackers.iter().map(Url::to_string).collect()]), | ||
nodes: None, | ||
comment: None, | ||
created_by: None, | ||
creation_date: None, | ||
encoding: None, | ||
info, | ||
}, | ||
None => return Err(Error::FromLinkNoInfo), | ||
}; | ||
|
||
let path = self.output.unwrap_or_else(|| { | ||
let mut path = PathBuf::new(); | ||
path.push(infohash.to_string()); | ||
path.set_extension("torrent"); | ||
path | ||
}); | ||
|
||
fs::File::create(&path) | ||
.context(error::Filesystem { path: path.clone() }) | ||
.and_then(|mut f| { | ||
f.write_all(&bendy::serde::ser::to_bytes(&metainfo)?) | ||
.context(error::Filesystem { path: path.clone() }) | ||
}) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn input_required() { | ||
test_env! { | ||
args: [ | ||
"torrent", | ||
"from-magnet", | ||
], | ||
tree: { | ||
}, | ||
matches: Err(Error::Clap { .. }), | ||
}; | ||
} | ||
|
||
#[test] | ||
fn test_no_info() { | ||
let tracker_url = "udp://1.2.3.4:1337"; | ||
let metainfo = Metainfo { | ||
announce: None, | ||
announce_list: Some(vec![vec![tracker_url.into()]]), | ||
nodes: None, | ||
comment: None, | ||
created_by: None, | ||
creation_date: None, | ||
encoding: None, | ||
info: Info { | ||
private: None, | ||
piece_length: Bytes(16 * 1024), | ||
source: None, | ||
name: "testing".into(), | ||
pieces: PieceList::from_pieces(["test", "data"]), | ||
mode: Mode::Single { | ||
length: Bytes(2 * 16 * 1024), | ||
md5sum: None, | ||
}, | ||
update_url: None, | ||
}, | ||
}; | ||
let link = MagnetLink::from_metainfo_lossy(&metainfo).unwrap(); | ||
let mut env = test_env! { | ||
args: [ | ||
"torrent", | ||
"from-link", | ||
link.to_url().as_str(), | ||
], | ||
tree: {}, | ||
}; | ||
assert_matches!(env.run(), Err(Error::FromLinkNoInfo)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters