diff --git a/CHANGELOG.md b/CHANGELOG.md index a06bcd6..49b0c40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 _ +- :sparkles: [`xxxxxxxxxxxx`](https://github.com/casey/intermodal/commits/master) Partially implement BEP 53 - Fixes [#245](https://github.com/casey/intermodal/issues/245) - _strickinato _ +- :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 _ - :wrench: [`ddf097c83690`](https://github.com/casey/intermodal/commit/ddf097c8369002748992165f81e9a1bdbe6eff98) Fix `publish` recipe ([#368](https://github.com/casey/intermodal/pull/368)) - _Casey Rodarmor _ diff --git a/book/src/commands/imdl-torrent-link.md b/book/src/commands/imdl-torrent-link.md index 81680e7..c066aad 100644 --- a/book/src/commands/imdl-torrent-link.md +++ b/book/src/commands/imdl-torrent-link.md @@ -14,8 +14,12 @@ FLAGS: -V, --version Print version number. OPTIONS: - -i, --input Generate magnet link from metainfo at `PATH`. If - `PATH` is `-`, read metainfo from standard input. - -p, --peer ... Add `PEER` to magnet link. + -s, --select-only ... + Specify files that torrent clients select for download. Values are + indices into the info.files list. e.g. `--select-only 1,2,3` + -i, --input + Generate magnet link from metainfo at `PATH`. If `PATH` is `-`, read + metainfo from standard input. + -p, --peer ... Add `PEER` to magnet link. ``` \ No newline at end of file diff --git a/completions/imdl.bash b/completions/imdl.bash index 0ec0514..bc59a28 100644 --- a/completions/imdl.bash +++ b/completions/imdl.bash @@ -245,7 +245,7 @@ _imdl() { return 0 ;; 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 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -268,6 +268,14 @@ _imdl() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --select-only) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -s) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; *) COMPREPLY=() ;; diff --git a/completions/imdl.elvish b/completions/imdl.elvish index 3f27170..ae6f602 100644 --- a/completions/imdl.elvish +++ b/completions/imdl.elvish @@ -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 -p '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 --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 -s 'Select files to download. Values are indices into the `info.files` list, e.g. `--select-only 1,2,3`.' + 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 --help 'Print help message.' cand -V 'Print version number.' diff --git a/completions/imdl.fish b/completions/imdl.fish index 722541c..7603dfa 100644 --- a/completions/imdl.fish +++ b/completions/imdl.fish @@ -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 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 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 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.' diff --git a/completions/imdl.powershell b/completions/imdl.powershell index 2db21f3..0c50388 100644 --- a/completions/imdl.powershell +++ b/completions/imdl.powershell @@ -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('-p', 'p', [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('--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('-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('--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('--help', 'help', [CompletionResultType]::ParameterName, 'Print help message.') [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version number.') diff --git a/completions/imdl.zsh b/completions/imdl.zsh index 4f25b96..60a625d 100644 --- a/completions/imdl.zsh +++ b/completions/imdl.zsh @@ -135,8 +135,10 @@ _arguments "${_arguments_options[@]}" \ '--input=[Generate magnet link from metainfo at `PATH`. If `PATH` is `-`, read metainfo from standard input.]' \ '*-p+[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]' \ -'--open[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`.]' \ +'*--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.]' \ '--help[Print help message.]' \ '-V[Print version number.]' \ diff --git a/man/imdl-torrent-link.1 b/man/imdl-torrent-link.1 index f91d81a..ceb7406 100644 --- a/man/imdl-torrent-link.1 +++ b/man/imdl-torrent-link.1 @@ -19,6 +19,10 @@ and `cmd \fI\,/C\/\fP start` on Windows Print version number. .SH "OPTIONS:" .TP +\fB\-s\fR, \fB\-\-select\-only\fR ... +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 Generate magnet link from metainfo at `PATH`. If `PATH` is `\-`, read metainfo from standard input. diff --git a/src/magnet_link.rs b/src/magnet_link.rs index 599711c..7939414 100644 --- a/src/magnet_link.rs +++ b/src/magnet_link.rs @@ -5,6 +5,7 @@ pub(crate) struct MagnetLink { name: Option, peers: Vec, trackers: Vec, + indices: BTreeSet, } impl MagnetLink { @@ -26,6 +27,7 @@ impl MagnetLink { name: None, peers: Vec::new(), trackers: Vec::new(), + indices: BTreeSet::new(), } } @@ -43,6 +45,10 @@ impl MagnetLink { self.trackers.push(tracker); } + pub(crate) fn add_index(&mut self, index: u64) { + self.indices.insert(index); + } + pub(crate) fn to_url(&self) -> Url { let mut url = Url::parse("magnet:").unwrap(); @@ -63,6 +69,16 @@ impl MagnetLink { 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 @@ -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] fn complex() { let mut link = MagnetLink::with_infohash(Infohash::from_bencoded_info_dict("".as_bytes())); diff --git a/src/subcommand/torrent/link.rs b/src/subcommand/torrent/link.rs index 69d1385..c618235 100644 --- a/src/subcommand/torrent/link.rs +++ b/src/subcommand/torrent/link.rs @@ -21,7 +21,7 @@ pub(crate) struct Link { long = "open", short = "O", 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, #[structopt( @@ -31,6 +31,15 @@ pub(crate) struct Link { help = "Add `PEER` to magnet link." )] peers: Vec, + #[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, } impl Link { @@ -52,6 +61,10 @@ impl Link { link.add_peer(peer); } + for index in self.indices { + link.add_index(index); + } + let url = link.to_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] fn infohash_correct_with_nonstandard_info_dict() { let mut env = test_env! {