Partially implement BEP 53

This enables `--select-only 1,2,3` which gets appended to the magnet
link as `&so=1,2,3`.

It's a partial implementation because we're missing support for file
ranges ie `--select-only 4-6`

type: added
fixes:
- https://github.com/casey/intermodal/issues/245
This commit is contained in:
strickinato 2020-04-12 19:50:00 -07:00 committed by Casey Rodarmor
parent 6185d6c8a2
commit cb8b5a6919
No known key found for this signature in database
GPG Key ID: 556186B153EC6FE0
10 changed files with 116 additions and 14 deletions

View File

@ -2,9 +2,10 @@ Changelog
========= =========
UNRELEASED - 2020-04-12 UNRELEASED - 2020-04-13
----------------------- -----------------------
- :books: [`xxxxxxxxxxxx`](https://github.com/casey/intermodal/commits/master) Add table of packages to readme ([#372](https://github.com/casey/intermodal/pull/372)) - Fixes [#369](https://github.com/casey/intermodal/issues/369) - _Casey Rodarmor <casey@rodarmor.com>_ - :sparkles: [`xxxxxxxxxxxx`](https://github.com/casey/intermodal/commits/master) Partially implement BEP 53 - Fixes [#245](https://github.com/casey/intermodal/issues/245) - _strickinato <aaronstrick@gmail.com>_
- :books: [`6185d6c8a27c`](https://github.com/casey/intermodal/commit/6185d6c8a27c0d603f0434e98000c8e4a868dcc8) Add table of packages to readme ([#372](https://github.com/casey/intermodal/pull/372)) - Fixes [#369](https://github.com/casey/intermodal/issues/369) - _Casey Rodarmor <casey@rodarmor.com>_
- :wrench: [`ddf097c83690`](https://github.com/casey/intermodal/commit/ddf097c8369002748992165f81e9a1bdbe6eff98) Fix `publish` recipe ([#368](https://github.com/casey/intermodal/pull/368)) - _Casey Rodarmor <casey@rodarmor.com>_ - :wrench: [`ddf097c83690`](https://github.com/casey/intermodal/commit/ddf097c8369002748992165f81e9a1bdbe6eff98) Fix `publish` recipe ([#368](https://github.com/casey/intermodal/pull/368)) - _Casey Rodarmor <casey@rodarmor.com>_

View File

@ -14,8 +14,12 @@ FLAGS:
-V, --version Print version number. -V, --version Print version number.
OPTIONS: OPTIONS:
-i, --input <METAINFO> Generate magnet link from metainfo at `PATH`. If -s, --select-only <INDICES>...
`PATH` is `-`, read metainfo from standard input. Specify files that torrent clients select for download. Values are
-p, --peer <PEER>... Add `PEER` to magnet link. indices into the info.files list. e.g. `--select-only 1,2,3`
-i, --input <METAINFO>
Generate magnet link from metainfo at `PATH`. If `PATH` is `-`, read
metainfo from standard input.
-p, --peer <PEER>... Add `PEER` to magnet link.
``` ```

View File

@ -245,7 +245,7 @@ _imdl() {
return 0 return 0
;; ;;
imdl__torrent__link) imdl__torrent__link)
opts=" -O -h -V -i -p --open --help --version --input --peer " opts=" -O -h -V -i -p -s --open --help --version --input --peer --select-only "
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
@ -268,6 +268,14 @@ _imdl() {
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
--select-only)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-s)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*) *)
COMPREPLY=() COMPREPLY=()
;; ;;

View File

@ -124,8 +124,10 @@ Sort in ascending order by size, break ties in descending path order:
cand --input 'Generate magnet link from metainfo at `PATH`. If `PATH` is `-`, read metainfo from standard input.' cand --input 'Generate magnet link from metainfo at `PATH`. If `PATH` is `-`, read metainfo from standard input.'
cand -p 'Add `PEER` to magnet link.' cand -p 'Add `PEER` to magnet link.'
cand --peer 'Add `PEER` to magnet link.' cand --peer 'Add `PEER` to magnet link.'
cand -O 'Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows' cand -s 'Select files to download. Values are indices into the `info.files` list, e.g. `--select-only 1,2,3`.'
cand --open 'Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows' cand --select-only 'Select files to download. Values are indices into the `info.files` list, e.g. `--select-only 1,2,3`.'
cand -O 'Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows.'
cand --open 'Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows.'
cand -h 'Print help message.' cand -h 'Print help message.'
cand --help 'Print help message.' cand --help 'Print help message.'
cand -V 'Print version number.' cand -V 'Print version number.'

View File

@ -69,7 +69,8 @@ complete -c imdl -n "__fish_seen_subcommand_from create" -l help -d 'Print help
complete -c imdl -n "__fish_seen_subcommand_from create" -s V -l version -d 'Print version number.' complete -c imdl -n "__fish_seen_subcommand_from create" -s V -l version -d 'Print version number.'
complete -c imdl -n "__fish_seen_subcommand_from link" -s i -l input -d 'Generate magnet link from metainfo at `PATH`. If `PATH` is `-`, read metainfo from standard input.' complete -c imdl -n "__fish_seen_subcommand_from link" -s i -l input -d 'Generate magnet link from metainfo at `PATH`. If `PATH` is `-`, read metainfo from standard input.'
complete -c imdl -n "__fish_seen_subcommand_from link" -s p -l peer -d 'Add `PEER` to magnet link.' complete -c imdl -n "__fish_seen_subcommand_from link" -s p -l peer -d 'Add `PEER` to magnet link.'
complete -c imdl -n "__fish_seen_subcommand_from link" -s O -l open -d 'Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows' complete -c imdl -n "__fish_seen_subcommand_from link" -s s -l select-only -d 'Select files to download. Values are indices into the `info.files` list, e.g. `--select-only 1,2,3`.'
complete -c imdl -n "__fish_seen_subcommand_from link" -s O -l open -d 'Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows.'
complete -c imdl -n "__fish_seen_subcommand_from link" -s h -l help -d 'Print help message.' complete -c imdl -n "__fish_seen_subcommand_from link" -s h -l help -d 'Print help message.'
complete -c imdl -n "__fish_seen_subcommand_from link" -s V -l version -d 'Print version number.' complete -c imdl -n "__fish_seen_subcommand_from link" -s V -l version -d 'Print version number.'
complete -c imdl -n "__fish_seen_subcommand_from piece-length" -s h -l help -d 'Print help message.' complete -c imdl -n "__fish_seen_subcommand_from piece-length" -s h -l help -d 'Print help message.'

View File

@ -132,8 +132,10 @@ Sort in ascending order by size, break ties in descending path order:
[CompletionResult]::new('--input', 'input', [CompletionResultType]::ParameterName, 'Generate magnet link from metainfo at `PATH`. If `PATH` is `-`, read metainfo from standard input.') [CompletionResult]::new('--input', 'input', [CompletionResultType]::ParameterName, 'Generate magnet link from metainfo at `PATH`. If `PATH` is `-`, read metainfo from standard input.')
[CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Add `PEER` to magnet link.') [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Add `PEER` to magnet link.')
[CompletionResult]::new('--peer', 'peer', [CompletionResultType]::ParameterName, 'Add `PEER` to magnet link.') [CompletionResult]::new('--peer', 'peer', [CompletionResultType]::ParameterName, 'Add `PEER` to magnet link.')
[CompletionResult]::new('-O', 'O', [CompletionResultType]::ParameterName, 'Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows') [CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Select files to download. Values are indices into the `info.files` list, e.g. `--select-only 1,2,3`.')
[CompletionResult]::new('--open', 'open', [CompletionResultType]::ParameterName, 'Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows') [CompletionResult]::new('--select-only', 'select-only', [CompletionResultType]::ParameterName, 'Select files to download. Values are indices into the `info.files` list, e.g. `--select-only 1,2,3`.')
[CompletionResult]::new('-O', 'O', [CompletionResultType]::ParameterName, 'Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows.')
[CompletionResult]::new('--open', 'open', [CompletionResultType]::ParameterName, 'Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows.')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help message.') [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help message.')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help message.') [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help message.')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version number.') [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version number.')

View File

@ -135,8 +135,10 @@ _arguments "${_arguments_options[@]}" \
'--input=[Generate magnet link from metainfo at `PATH`. If `PATH` is `-`, read metainfo from standard input.]' \ '--input=[Generate magnet link from metainfo at `PATH`. If `PATH` is `-`, read metainfo from standard input.]' \
'*-p+[Add `PEER` to magnet link.]' \ '*-p+[Add `PEER` to magnet link.]' \
'*--peer=[Add `PEER` to magnet link.]' \ '*--peer=[Add `PEER` to magnet link.]' \
'-O[Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows]' \ '*-s+[Select files to download. Values are indices into the `info.files` list, e.g. `--select-only 1,2,3`.]' \
'--open[Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows]' \ '*--select-only=[Select files to download. Values are indices into the `info.files` list, e.g. `--select-only 1,2,3`.]' \
'-O[Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows.]' \
'--open[Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; `open` on macOS; and `cmd /C start` on Windows.]' \
'-h[Print help message.]' \ '-h[Print help message.]' \
'--help[Print help message.]' \ '--help[Print help message.]' \
'-V[Print version number.]' \ '-V[Print version number.]' \

View File

@ -19,6 +19,10 @@ and `cmd \fI\,/C\/\fP start` on Windows
Print version number. Print version number.
.SH "OPTIONS:" .SH "OPTIONS:"
.TP .TP
\fB\-s\fR, \fB\-\-select\-only\fR <INDICES>...
Specify files that torrent clients select for download. Values are indices into
the info.files list. e.g. `\-\-select\-only 1,2,3`
.TP
\fB\-i\fR, \fB\-\-input\fR <METAINFO> \fB\-i\fR, \fB\-\-input\fR <METAINFO>
Generate magnet link from metainfo at `PATH`. If `PATH` is `\-`, read metainfo from Generate magnet link from metainfo at `PATH`. If `PATH` is `\-`, read metainfo from
standard input. standard input.

View File

@ -5,6 +5,7 @@ pub(crate) struct MagnetLink {
name: Option<String>, name: Option<String>,
peers: Vec<HostPort>, peers: Vec<HostPort>,
trackers: Vec<Url>, trackers: Vec<Url>,
indices: BTreeSet<u64>,
} }
impl MagnetLink { impl MagnetLink {
@ -26,6 +27,7 @@ impl MagnetLink {
name: None, name: None,
peers: Vec::new(), peers: Vec::new(),
trackers: Vec::new(), trackers: Vec::new(),
indices: BTreeSet::new(),
} }
} }
@ -43,6 +45,10 @@ impl MagnetLink {
self.trackers.push(tracker); self.trackers.push(tracker);
} }
pub(crate) fn add_index(&mut self, index: u64) {
self.indices.insert(index);
}
pub(crate) fn to_url(&self) -> Url { pub(crate) fn to_url(&self) -> Url {
let mut url = Url::parse("magnet:").unwrap(); let mut url = Url::parse("magnet:").unwrap();
@ -63,6 +69,16 @@ impl MagnetLink {
query.push_str(&peer.to_string()); query.push_str(&peer.to_string());
} }
if !self.indices.is_empty() {
query.push_str("&so=");
for (i, selection_index) in self.indices.iter().enumerate() {
if i > 0 {
query.push(',');
}
query.push_str(&selection_index.to_string());
}
}
url.set_query(Some(&query)); url.set_query(Some(&query));
url url
@ -129,6 +145,19 @@ mod tests {
); );
} }
#[test]
fn with_indices() {
let mut link = MagnetLink::with_infohash(Infohash::from_bencoded_info_dict("".as_bytes()));
link.add_index(4);
link.add_index(6);
link.add_index(6);
link.add_index(2);
assert_eq!(
link.to_url().as_str(),
"magnet:?xt=urn:btih:da39a3ee5e6b4b0d3255bfef95601890afd80709&so=2,4,6"
);
}
#[test] #[test]
fn complex() { fn complex() {
let mut link = MagnetLink::with_infohash(Infohash::from_bencoded_info_dict("".as_bytes())); let mut link = MagnetLink::with_infohash(Infohash::from_bencoded_info_dict("".as_bytes()));

View File

@ -21,7 +21,7 @@ pub(crate) struct Link {
long = "open", long = "open",
short = "O", short = "O",
help = "Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; \ help = "Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; \
`open` on macOS; and `cmd /C start` on Windows" `open` on macOS; and `cmd /C start` on Windows."
)] )]
open: bool, open: bool,
#[structopt( #[structopt(
@ -31,6 +31,15 @@ pub(crate) struct Link {
help = "Add `PEER` to magnet link." help = "Add `PEER` to magnet link."
)] )]
peers: Vec<HostPort>, peers: Vec<HostPort>,
#[structopt(
long = "select-only",
short = "s",
value_name = "INDICES",
use_delimiter = true,
help = "Select files to download. Values are indices into the `info.files` list, e.g. \
`--select-only 1,2,3`."
)]
indices: Vec<u64>,
} }
impl Link { impl Link {
@ -52,6 +61,10 @@ impl Link {
link.add_peer(peer); link.add_peer(peer);
} }
for index in self.indices {
link.add_index(index);
}
let url = link.to_url(); let url = link.to_url();
outln!(env, "{}", url)?; outln!(env, "{}", url)?;
@ -194,6 +207,42 @@ mod tests {
); );
} }
#[test]
fn with_indices() {
let mut env = test_env! {
args: [
"torrent",
"link",
"--input",
"foo.torrent",
"--select-only",
"2,4",
"--select-only",
"4,6",
],
tree: {
"foo.torrent": "d\
8:announce24:https://foo.com/announce\
4:infod6:lengthi0e4:name3:foo12:piece lengthi1e6:pieces0:e\
e",
}
};
env.assert_ok();
const INFO: &str = "d6:lengthi0e4:name3:foo12:piece lengthi1e6:pieces0:e";
let infohash = Sha1Digest::from_data(INFO.as_bytes());
assert_eq!(
env.out(),
format!(
"magnet:?xt=urn:btih:{}&dn=foo&tr=https://foo.com/announce&so=2,4,6\n",
infohash
),
);
}
#[test] #[test]
fn infohash_correct_with_nonstandard_info_dict() { fn infohash_correct_with_nonstandard_info_dict() {
let mut env = test_env! { let mut env = test_env! {