From 98eb857794d4ea69633e2f4240a2a4705dd0fa1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20F=C3=A4hrmann?= Date: Sat, 14 Feb 2026 11:25:08 +0100 Subject: [PATCH] [pp:exec] use non-UNC path replacements (#8879) provide '{_path_unc}' and '{_directory_unc}' replacement fields --- docs/options.md | 6 ++++-- gallery_dl/option.py | 3 ++- gallery_dl/postprocessor/exec.py | 31 +++++++++++++++++++------------ test/test_postprocessor.py | 11 +++++++---- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/docs/options.md b/docs/options.md index a8bd8b28..86d9654e 100644 --- a/docs/options.md +++ b/docs/options.md @@ -216,8 +216,10 @@ 'vp8', 'vp9', 'vp9-lossless', 'copy', 'zip'. --exec CMD Execute CMD for each downloaded file. Supported replacement fields are {} or {_path}, - {_directory}, {_filename}. Example: --exec - "convert {} {}.png && rm {}" + {_temppath}, {_directory}, {_filename}. On + Windows, use {_path_unc} or {_directory_unc} + for UNC paths. Example: --exec "convert {} + {}.png && rm {}" --exec-after CMD Execute CMD after all files were downloaded. Example: --exec-after "cd {_directory} && convert * ../doc.pdf" diff --git a/gallery_dl/option.py b/gallery_dl/option.py index 6f5abe6b..928d80b3 100644 --- a/gallery_dl/option.py +++ b/gallery_dl/option.py @@ -899,7 +899,8 @@ def build_parser(): action=AppendCommandAction, const={"name": "exec"}, help=("Execute CMD for each downloaded file. " "Supported replacement fields are " - "{} or {_path}, {_directory}, {_filename}. " + "{} or {_path}, {_temppath}, {_directory}, {_filename}. " + "On Windows, use {_path_unc} or {_directory_unc} for UNC paths. " "Example: --exec \"convert {} {}.png && rm {}\""), ) postprocessor.add_argument( diff --git a/gallery_dl/postprocessor/exec.py b/gallery_dl/postprocessor/exec.py index 9e2e4df4..ea0bdb70 100644 --- a/gallery_dl/postprocessor/exec.py +++ b/gallery_dl/postprocessor/exec.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright 2018-2023 Mike Fährmann +# Copyright 2018-2026 Mike Fährmann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -61,7 +61,8 @@ class ExecPP(PostProcessor): def _prepare_cmd(self, cmd): if isinstance(cmd, str): self._sub = util.re( - r"\{(_directory|_filename|_(?:temp)?path|)\}").sub + r"(?i)\{(_directory(?:_unc)?|_filename" + r"|_(?:temp)?path(?:_unc)?|)\}").sub return self.exec_string, cmd else: return self.exec_list, [formatter.parse(arg) for arg in cmd] @@ -73,10 +74,13 @@ class ExecPP(PostProcessor): if archive and archive.check(kwdict): return - kwdict["_directory"] = pathfmt.realdirectory + kwdict["_directory"] = pathfmt.directory kwdict["_filename"] = pathfmt.filename kwdict["_temppath"] = pathfmt.temppath - kwdict["_path"] = pathfmt.realpath + kwdict["_path"] = pathfmt.path + if util.WINDOWS: + kwdict["_directory_unc"] = pathfmt.realdirectory + kwdict["_path_unc"] = pathfmt.realpath args = [arg.format_map(kwdict) for arg in self.args] args[0] = os.path.expanduser(args[0]) @@ -133,14 +137,17 @@ class ExecPP(PostProcessor): ) def _replace(self, match): - name = match[1] - if name == "_directory": - return quote(self.pathfmt.realdirectory) - if name == "_filename": - return quote(self.pathfmt.filename) - if name == "_temppath": - return quote(self.pathfmt.temppath) - return quote(self.pathfmt.realpath) + attr = { + "" : "path", + "_path" : "path", + "_path_unc" : "realpath", + "_temppath" : "temppath", + "_temppath_unc" : "temppath", + "_directory" : "directory", + "_directory_unc": "realdirectory", + "_filename" : "filename", + }[match[1].lower()] + return quote(getattr(self.pathfmt, attr)) __postprocessor__ = ExecPP diff --git a/test/test_postprocessor.py b/test/test_postprocessor.py index 7a830a15..9b797b71 100644 --- a/test/test_postprocessor.py +++ b/test/test_postprocessor.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2019-2025 Mike Fährmann +# Copyright 2019-2026 Mike Fährmann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -196,7 +196,8 @@ class ExecTest(BasePostprocessorTest): def test_command_string(self): self._create({ - "command": "echo {} {_path} {_temppath} {_directory} {_filename} " + "command": "echo {} {_path} {_path_unc} {_temppath} " + "{_directory} {_directory_unc} {_filename} " "&& rm {};", }) @@ -208,12 +209,14 @@ class ExecTest(BasePostprocessorTest): p.assert_called_once_with( (f"echo " - f"{self.pathfmt.realpath} " + f"{self.pathfmt.path} " + f"{self.pathfmt.path} " f"{self.pathfmt.realpath} " f"{self.pathfmt.temppath} " + f"{self.pathfmt.directory} " f"{self.pathfmt.realdirectory} " f"{self.pathfmt.filename} " - f"&& rm {self.pathfmt.realpath};"), + f"&& rm {self.pathfmt.path};"), shell=True, creationflags=0, start_new_session=False,