Compare commits

...

5 Commits

@ -1,6 +1,7 @@
## v1.2.1-dev ## v1.2.1-dev
- Fix `NO_COLOR` support. - Fix `NO_COLOR` support.
- The `d` keyboard shortcut will now download the current page to disk.
## v1.2.0 ## v1.2.0

@ -112,6 +112,51 @@ fn clean_response(res: &mut String) {
}) })
} }
/// Downloads menu or text to disk as `filename`.
/// Allows canceling with Ctrl-c, but it's
/// kind of hacky - needs the UI receiver passed in.
/// Returns a tuple of:
/// (path it was saved to, the size in bytes)
pub fn download_url_with_filename(
url: &str,
tls: bool,
tor: bool,
chan: ui::KeyReceiver,
filename: &str,
) -> Result<(String, usize)> {
let u = parse_url(url);
let mut path = std::path::PathBuf::from(".");
path.push(filename);
let mut stream = request(u.host, u.port, u.sel, tls, tor)?;
let mut file = fs::OpenOptions::new()
.write(true)
.create_new(true)
.append(true)
.open(&path)
.map_err(|e| error!("{}", e))?;
let mut buf = [0; 1024];
let mut bytes = 0;
while let Ok(count) = stream.read(&mut buf) {
if count == 0 {
break;
}
bytes += count;
file.write_all(&buf[..count])?;
if let Ok(chan) = chan.lock() {
if let Ok(Key::Ctrl('c')) = chan.try_recv() {
if path.exists() {
fs::remove_file(path)?;
}
return Err(error!("Download cancelled"));
}
}
}
Ok((filename.to_string(), bytes))
}
/// Downloads a binary to disk. Allows canceling with Ctrl-c, but it's /// Downloads a binary to disk. Allows canceling with Ctrl-c, but it's
/// kind of hacky - needs the UI receiver passed in. /// kind of hacky - needs the UI receiver passed in.
/// Returns a tuple of: /// Returns a tuple of:

@ -123,6 +123,7 @@ is save bookmark
ia show history ia show history
i i
ir view raw source ir view raw source
id download raw source
iw toggle wide mode iw toggle wide mode
ie toggle encoding ie toggle encoding
iq quit phetch iq quit phetch

@ -236,6 +236,30 @@ impl UI {
}) })
} }
/// Used to download content of the current view with a provided filename
fn download_file_with_filename(&mut self, url: &str, filename: String) -> Result<()> {
let url = url.to_string();
let (tls, tor) = (
self.config.read().unwrap().tls,
self.config.read().unwrap().tor,
);
let chan = self.keys.clone();
self.spinner(&format!("Downloading {}", url), move || {
gopher::download_url_with_filename(&url, tls, tor, chan, &filename)
})
.and_then(|res| res)
.map(|(path, bytes)| {
self.set_status(
format!(
"Download complete! {} saved to {}",
utils::human_bytes(bytes),
path
)
.as_ref(),
);
})
}
/// Download a binary file. Used by `open()` internally. /// Download a binary file. Used by `open()` internally.
fn download(&mut self, url: &str) -> Result<()> { fn download(&mut self, url: &str) -> Result<()> {
let url = url.to_string(); let url = url.to_string();
@ -659,6 +683,29 @@ impl UI {
Action::Keypress(Key::Char(key)) | Action::Keypress(Key::Ctrl(key)) => match key { Action::Keypress(Key::Char(key)) | Action::Keypress(Key::Ctrl(key)) => match key {
'a' => self.open("History", "gopher://phetch/1/history")?, 'a' => self.open("History", "gopher://phetch/1/history")?,
'b' => self.open("Bookmarks", "gopher://phetch/1/bookmarks")?, 'b' => self.open("Bookmarks", "gopher://phetch/1/bookmarks")?,
'd' => {
let url = match self.views.get(self.focused) {
Some(view) => String::from(view.url()),
None => return Err(error!("Could not get URL from view")),
};
let url = url.as_str();
if url.starts_with("gopher://phetch/") {
return Err(error!("Can't download internal phetch pages."));
}
let u = gopher::parse_url(&url);
let default_filename = u.sel.split_terminator('/').rev().next().unwrap_or("");
if let Some(filename) = self.prompt("Save to disk as: ", default_filename) {
if filename.trim().is_empty() {
return Err(error!("Please provide a filename."));
}
match self.download_file_with_filename(url, String::from(filename)) {
Ok(()) => (),
Err(e) => return Err(error!("Download failed: {}", e)),
}
}
}
'g' => { 'g' => {
if let Some(url) = self.prompt("Go to URL: ", "") { if let Some(url) = self.prompt("Go to URL: ", "") {
self.open(&url, &url)?; self.open(&url, &url)?;

Loading…
Cancel
Save