~nhanb/mcross-devel

Download binary files v1 PROPOSED

Frederick Yin: 1
 Download binary files

 3 files changed, 59 insertions(+), 5 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~nhanb/mcross-devel/patches/11553/mbox | git am -3
Learn more about email & git

[PATCH] Download binary files Export this patch

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