From 788db47b95b8478aef5276f2c00243576f2719e7 Mon Sep 17 00:00:00 2001 From: ed Date: Sun, 4 Sep 2022 01:42:28 +0200 Subject: [PATCH] option to let mtp's keep stdout/stderr --- README.md | 9 ++++++--- bin/mtag/very-bad-idea.py | 2 +- copyparty/mtag.py | 12 +++++++++++- copyparty/util.py | 10 +++++++--- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6c00607c..301957dc 100644 --- a/README.md +++ b/README.md @@ -826,8 +826,11 @@ copyparty can invoke external programs to collect additional metadata for files * "audio file" also means videos btw, as long as there is an audio stream * `-mtp ext=an,~/bin/file-ext.py` runs `~/bin/file-ext.py` to get the `ext` tag only if file is not audio (`an`) * `-mtp arch,built,ver,orig=an,eexe,edll,~/bin/exe.py` runs `~/bin/exe.py` to get properties about windows-binaries only if file is not audio (`an`) and file extension is exe or dll - -you can control how the parser is killed if it times out with option `kt` killing the entire process tree (default), `km` just the main process, or `kn` let it continue running until copyparty is terminated +* if you want to daisychain parsers, use the `p` flag to set processing order + * `-mtp foo=p1,~/a.py` runs before `-mtp foo=p2,~/b.py` and will forward all the tags detected so far as json to the stdin of b.py +* option `c0` disables capturing of stdout/stderr, so copyparty will not receive any tags from the process at all -- instead the invoked program is free to print whatever to the console, just using copyparty as a launcher + * `c1` captures stdout only, `c2` only stderr, and `c3` (default) captures both +* you can control how the parser is killed if it times out with option `kt` killing the entire process tree (default), `km` just the main process, or `kn` let it continue running until copyparty is terminated if something doesn't work, try `--mtag-v` for verbose error messages @@ -846,7 +849,7 @@ that'll run the command `notify-send` with the path to the uploaded file as the note that it will only trigger on new unique files, not dupes -and it will occupy the parsing threads, so fork anything expensive, or if you want to intentionally queue/singlethread you can combine it with `--mtag-mt 1` +and it will occupy the parsing threads, so fork anything expensive (or set `kn` to have copyparty fork it for you) -- otoh if you want to intentionally queue/singlethread you can combine it with `--mtag-mt 1` if this becomes popular maybe there should be a less janky way to do it actually diff --git a/bin/mtag/very-bad-idea.py b/bin/mtag/very-bad-idea.py index 0885f54e..a03bc05e 100755 --- a/bin/mtag/very-bad-idea.py +++ b/bin/mtag/very-bad-idea.py @@ -16,7 +16,7 @@ goes without saying, but this is HELLA DANGEROUS, GIVES RCE TO ANYONE WHO HAVE UPLOAD PERMISSIONS example copyparty config to use this: - --urlform save,get -v.::w:c,e2d,e2t,mte=+a1:c,mtp=a1=ad,kn,bin/mtag/very-bad-idea.py + --urlform save,get -v.::w:c,e2d,e2t,mte=+a1:c,mtp=a1=ad,kn,c0,bin/mtag/very-bad-idea.py recommended deps: apt install xdotool libnotify-bin diff --git a/copyparty/mtag.py b/copyparty/mtag.py index ee8b098f..ac958440 100644 --- a/copyparty/mtag.py +++ b/copyparty/mtag.py @@ -45,6 +45,7 @@ class MParser(object): self.timeout = 30 self.force = False self.kill = "t" # tree; all children recursively + self.capture = 3 # outputs to consume self.audio = "y" self.pri = 0 # priority; higher = later self.ext = [] @@ -72,6 +73,10 @@ class MParser(object): self.kill = arg[1:] # [t]ree [m]ain [n]one continue + if arg.startswith("c"): + self.capture = int(arg[1:]) # 0=none 1=stdout 2=stderr 3=both + continue + if arg == "f": self.force = True continue @@ -519,7 +524,12 @@ class MTag(object): if parser.bin.endswith(".py"): cmd = [sys.executable] + cmd - args = {"env": env, "timeout": parser.timeout, "kill": parser.kill} + args = { + "env": env, + "timeout": parser.timeout, + "kill": parser.kill, + "capture": parser.capture, + } if parser.pri: zd = oth_tags.copy() diff --git a/copyparty/util.py b/copyparty/util.py index 4eaf9cd1..660ba03f 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -1862,12 +1862,16 @@ def runcmd( argv: Union[list[bytes], list[str]], timeout: Optional[int] = None, **ka: Any ) -> tuple[int, str, str]: kill = ka.pop("kill", "t") # [t]ree [m]ain [n]one + capture = ka.pop("capture", 3) # 0=none 1=stdout 2=stderr 3=both sin = ka.pop("sin", None) if sin: ka["stdin"] = sp.PIPE - p = sp.Popen(argv, stdout=sp.PIPE, stderr=sp.PIPE, **ka) + cout = sp.PIPE if capture in [1, 3] else None + cerr = sp.PIPE if capture in [2, 3] else None + + p = sp.Popen(argv, stdout=cout, stderr=cerr, **ka) if not timeout or PY2: stdout, stderr = p.communicate(sin) else: @@ -1887,8 +1891,8 @@ def runcmd( stdout = b"" stderr = b"" - stdout = stdout.decode("utf-8", "replace") - stderr = stderr.decode("utf-8", "replace") + stdout = stdout.decode("utf-8", "replace") if cout else b"" + stderr = stderr.decode("utf-8", "replace") if cerr else b"" rc = p.returncode if rc is None: