Summary:
Instead of reinventing the wheel and implementing downloading logic on
our own, I turned to the so-called "helpers" for help - CLI
commands that handle the dirty work of download jobs.
Changes:
- Introduced config arguments `download-cmd` and `download-dest`
- Download helper, of course
- Documentation regarding downloads
- Crossed out binary file handling on the bucket list
---
README.md | 20 ++++++++++++++++++--
src/mcross/conf.py | 13 ++++++++++---
src/mcross/gui/controller.py | 31 +++++++++++++++++++++++++++++++
3 files changed, 59 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index c6b6474..e91f8b1 100644
--- a/README.md
+++ b/README.md
@@ -48,12 +48,28 @@ text-font = "Ubuntu"
The priority is CLI arg > config file > default.
-Keyboard shortcuts:
+## Keyboard shortcuts:
- `Ctrl-l`: jump to address bar.
- Hold `Alt` to see possible button shortcuts underlined. This is what Qt calls
[Accelerator Keys](https://doc.qt.io/qt-5/accelerators.html).
+## Downloading files:
+
+When the server responds with something that isn't decodable as a string,
+McRoss will download the response body as a file with a helper, for example,
+[gemget](https://github.com/makeworld-the-better-one/gemget/).
+
+Configuration:
+
+```toml
+# example for gemget; $URL is self-explanatory, and $DEST is just download-dest
+# i.e. where the files will be downloaded to.
+download-cmd = "gemget $URL -d $DEST"
+download-dest = "~/Downloads/"
+```
+
+The download job will then be handed to the helper of your choice.
# Development
@@ -91,7 +107,7 @@ necessarily agree with its "plaintext or nothing" stance.
- [x] more visual indicators: waiting cursor, status bar
- [x] parse gemini's advanced line types
- [x] render `text/*` mime types with correct charset
-- [ ] handle `binary/*` mime types
+- [x] handle `binary/*` mime types
- [x] configurable document styling
- [ ] human-friendly distribution
- [ ] TOFU TLS (right now it always accepts self-signed certs)
diff --git a/src/mcross/conf.py b/src/mcross/conf.py
index f37c43a..04341e7 100644
--- a/src/mcross/conf.py
+++ b/src/mcross/conf.py
@@ -20,6 +20,8 @@ conf_definitions = [
ConfDef("text-color", "t", str, "black"),
ConfDef("link-color", "l", str, "brown"),
ConfDef("list-item-color", "i", str, "#044604"),
+ ConfDef("download-cmd", None, str, ""),
+ ConfDef("download-dest", None, str, "~/Downloads/"),
]
@@ -59,9 +61,14 @@ def load_conf_file():
def parse_conf_args():
argparser = argparse.ArgumentParser()
for confdef in conf_definitions:
- argparser.add_argument(
- f"-{confdef.short_name}", f"--{confdef.name}", type=confdef.type,
- )
+ if confdef.short_name is not None:
+ argparser.add_argument(
+ f"-{confdef.short_name}", f"--{confdef.name}", type=confdef.type,
+ )
+ else:
+ argparser.add_argument(
+ f"--{confdef.name}", type=confdef.type,
+ )
args = argparser.parse_args()
return {key.replace("_", "-"): val for key, val in vars(args).items() if val}
diff --git a/src/mcross/gui/controller.py b/src/mcross/gui/controller.py
index 6397404..4b6c889 100644
--- a/src/mcross/gui/controller.py
+++ b/src/mcross/gui/controller.py
@@ -2,11 +2,14 @@ import logging
import threading
import time
import traceback
+from pathlib import Path
+import subprocess
from ssl import SSLCertVerificationError
from tkinter import READABLE, Tk, messagebox
import curio
+from .. import conf
from ..transport import (
MAX_REQUEST_SECONDS,
GeminiUrl,
@@ -176,6 +179,34 @@ class Controller:
"text/plain",
)
return resp
+ except UnicodeDecodeError:
+ # try downloading as file instead
+ # with the help of an external downloader, e.g. gemget
+ try:
+ if not conf.get("download-cmd") or not conf.get("download-dest"):
+ await self.put_gui_op(
+ statusbar_logger.info, "Download configuration incomplete"
+ )
+ return resp
+ download_cmd = conf.get("download-cmd").split(" ")
+ download_dest = Path(conf.get("download-dest")).expanduser()
+ except KeyError:
+ await self.put_gui_op(
+ statusbar_logger.info, "Download configuration not found"
+ )
+
+ # fill in arguments
+ for index, fragment in enumerate(download_cmd):
+ if fragment == "$URL":
+ download_cmd[index] = str(url)
+ elif fragment == "$DEST":
+ download_cmd[index] = str(download_dest)
+
+ proc = subprocess.run(download_cmd)
+ await self.put_gui_op(
+ statusbar_logger.info, f"Download initiated: {url}"
+ )
+ return resp
# Sucessfully decoded body string!
if resp.status.startswith("2"):
--
2.27.0
On July 11, 2020 1:36:04 PM UTC, Frederick Yin <fkfd@macaw.me> wrote:
>Summary:
>Instead of reinventing the wheel and implementing downloading logic on
>our own, I turned to the so-called "helpers" for help - CLI
>commands that handle the dirty work of download jobs.
>
>Changes:
>- Introduced config arguments `download-cmd` and `download-dest`
>- Download helper, of course
>- Documentation regarding downloads
>- Crossed out binary file handling on the bucket list
>---
> README.md | 20 ++++++++++++++++++--
> src/mcross/conf.py | 13 ++++++++++---
> src/mcross/gui/controller.py | 31 +++++++++++++++++++++++++++++++
> 3 files changed, 59 insertions(+), 5 deletions(-)
>
>diff --git a/README.md b/README.md
>index c6b6474..e91f8b1 100644
>--- a/README.md
>+++ b/README.md
>@@ -48,12 +48,28 @@ text-font = "Ubuntu"
>
> The priority is CLI arg > config file > default.
>
>-Keyboard shortcuts:
>+## Keyboard shortcuts:
>
> - `Ctrl-l`: jump to address bar.
>- Hold `Alt` to see possible button shortcuts underlined. This is what
>Qt calls
> [Accelerator Keys](https://doc.qt.io/qt-5/accelerators.html).
>
>+## Downloading files:
>+
>+When the server responds with something that isn't decodable as a
>string,
>+McRoss will download the response body as a file with a helper, for
>example,
>+[gemget](https://github.com/makeworld-the-better-one/gemget/).
>+
>+Configuration:
>+
>+```toml
>+# example for gemget; $URL is self-explanatory, and $DEST is just
>download-dest
>+# i.e. where the files will be downloaded to.
>+download-cmd = "gemget $URL -d $DEST"
>+download-dest = "~/Downloads/"
>+```
>+
>+The download job will then be handed to the helper of your choice.
>
> # Development
>
>@@ -91,7 +107,7 @@ necessarily agree with its "plaintext or nothing"
>stance.
> - [x] more visual indicators: waiting cursor, status bar
> - [x] parse gemini's advanced line types
> - [x] render `text/*` mime types with correct charset
>-- [ ] handle `binary/*` mime types
>+- [x] handle `binary/*` mime types
> - [x] configurable document styling
> - [ ] human-friendly distribution
> - [ ] TOFU TLS (right now it always accepts self-signed certs)
>diff --git a/src/mcross/conf.py b/src/mcross/conf.py
>index f37c43a..04341e7 100644
>--- a/src/mcross/conf.py
>+++ b/src/mcross/conf.py
>@@ -20,6 +20,8 @@ conf_definitions = [
> ConfDef("text-color", "t", str, "black"),
> ConfDef("link-color", "l", str, "brown"),
> ConfDef("list-item-color", "i", str, "#044604"),
>+ ConfDef("download-cmd", None, str, ""),
>+ ConfDef("download-dest", None, str, "~/Downloads/"),
> ]
>
>
>@@ -59,9 +61,14 @@ def load_conf_file():
> def parse_conf_args():
> argparser = argparse.ArgumentParser()
> for confdef in conf_definitions:
>- argparser.add_argument(
>- f"-{confdef.short_name}", f"--{confdef.name}",
>type=confdef.type,
>- )
>+ if confdef.short_name is not None:
>+ argparser.add_argument(
>+ f"-{confdef.short_name}", f"--{confdef.name}",
>type=confdef.type,
>+ )
>+ else:
>+ argparser.add_argument(
>+ f"--{confdef.name}", type=confdef.type,
>+ )
> args = argparser.parse_args()
>return {key.replace("_", "-"): val for key, val in vars(args).items()
>if val}
>
>diff --git a/src/mcross/gui/controller.py
>b/src/mcross/gui/controller.py
>index 6397404..4b6c889 100644
>--- a/src/mcross/gui/controller.py
>+++ b/src/mcross/gui/controller.py
>@@ -2,11 +2,14 @@ import logging
> import threading
> import time
> import traceback
>+from pathlib import Path
>+import subprocess
> from ssl import SSLCertVerificationError
> from tkinter import READABLE, Tk, messagebox
>
> import curio
>
>+from .. import conf
> from ..transport import (
> MAX_REQUEST_SECONDS,
> GeminiUrl,
>@@ -176,6 +179,34 @@ class Controller:
> "text/plain",
> )
> return resp
>+ except UnicodeDecodeError:
>+ # try downloading as file instead
>+ # with the help of an external downloader, e.g. gemget
>+ try:
>+ if not conf.get("download-cmd") or not
>conf.get("download-dest"):
>+ await self.put_gui_op(
>+ statusbar_logger.info, "Download
>configuration incomplete"
>+ )
>+ return resp
>+ download_cmd = conf.get("download-cmd").split(" ")
>+ download_dest =
>Path(conf.get("download-dest")).expanduser()
>+ except KeyError:
>+ await self.put_gui_op(
>+ statusbar_logger.info, "Download configuration
>not found"
>+ )
>+
>+ # fill in arguments
>+ for index, fragment in enumerate(download_cmd):
>+ if fragment == "$URL":
>+ download_cmd[index] = str(url)
>+ elif fragment == "$DEST":
>+ download_cmd[index] = str(download_dest)
>+
>+ proc = subprocess.run(download_cmd)
>+ await self.put_gui_op(
>+ statusbar_logger.info, f"Download initiated:
>{url}"
>+ )
>+ return resp
>
> # Sucessfully decoded body string!
> if resp.status.startswith("2"):
Oh wait. Please do not merge it. I need to edit the dynamic argument logic to parse $URL and $DEST when they aren't individual words, i.e. surrounded by spaces.
~fkfd