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
--
2.27.0