~nhanb/mcross-devel

Download binary files v2 PROPOSED

Frederick Yin: 1
 Download binary files

 3 files changed, 60 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/11570/mbox | git am -3
Learn more about email & git

[PATCH v2] 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
- Short names now optional for CLI args
---
New changes:
Handle scenarios where $URL and $DEST aren't single "words" in command.
Helper commands are now run as real shell commands, without subprocess
coming in to intervene.

 README.md                    | 24 ++++++++++++++++++++++--
 src/mcross/conf.py           | 13 ++++++++++---
 src/mcross/gui/controller.py | 28 ++++++++++++++++++++++++++++
 3 files changed, 60 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index c6b6474..6269117 100644
--- a/README.md
+++ b/README.md
@@ -48,12 +48,32 @@ 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.

Note: if your `download-dest` contains spaces, you need to escape them with
**2 backslashes**. Like this: `~/My\\ Downloads`. One for shell and one for
TOML.

# Development

@@ -91,7 +111,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..dde8c76 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,31 @@ 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_dest = Path(conf.get("download-dest")).expanduser()
                    download_cmd = (
                        conf.get("download-cmd")
                        .replace("$URL", str(url))
                        .replace("$DEST", str(download_dest))
                    )
                except KeyError:
                    await self.put_gui_op(
                        statusbar_logger.info, "Download configuration not found"
                    )

                proc = subprocess.run(download_cmd, shell=True)
                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