Skip to content

Commit

Permalink
[postprocessor:exec] improve (#421, #413)
Browse files Browse the repository at this point in the history
- add 'final' option
- include job status in pp finalization
- improve and extend documentation
  • Loading branch information
mikf committed Nov 3, 2019
1 parent c18fadc commit 9e88e7a
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 16 deletions.
27 changes: 23 additions & 4 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1388,12 +1388,31 @@ Description Controls whether to wait for a subprocess to finish
exec.command
------------
=========== =====
Type ``list`` of ``strings``
Example ``["echo", "{user[account]}", "{id}"]``
Type ``string`` or ``list`` of ``strings``
Example * ``"convert {} {}.png && rm {}"``
* ``["echo", "{user[account]}", "{id}"]``
Description The command to run.

Each element of this list is treated as a `format string`_ using
the files' metadata.
* If this is a ``string``, it will be executed using the system's
shell, e.g. ``/bin/sh``. Any ``{}`` will be replaced
with the full path of a file or target directory, depending on
`exec.final`_

* If this is a ``list``, the first element specifies the program
name and any further elements its arguments.
Each element of this list is treated as a `format string`_ using
the files' metadata as well as ``{_path}``, ``{_directory}``,
and ``{_filename}``.
=========== =====

exec.final
----------
=========== =====
Type ``bool``
Default ``false``
Description Controls whether to execute `exec.command`_ for each
downloaded file or only once after all files
have been downloaded successfully.
=========== =====


Expand Down
13 changes: 9 additions & 4 deletions gallery_dl/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ def run(self):
exc.__class__.__name__, exc)
log.debug("", exc_info=True)
self.status |= 1
except BaseException:
self.status |= 1
raise
finally:
self.handle_finalize()
return self.status
Expand Down Expand Up @@ -255,13 +258,15 @@ def handle_queue(self, url, kwdict):
self._write_unsupported(url)

def handle_finalize(self):
if self.postprocessors:
for pp in self.postprocessors:
pp.finalize()
pathfmt = self.pathfmt
if self.archive:
self.archive.close()
if self.pathfmt:
if pathfmt:
self.extractor._store_cookies()
if self.postprocessors:
status = self.status
for pp in self.postprocessors:
pp.run_final(pathfmt, status)

def handle_skip(self):
self.out.skip(self.pathfmt.path)
Expand Down
4 changes: 2 additions & 2 deletions gallery_dl/postprocessor/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ def run_after(pathfmt):
"""Execute postprocessor after moving a file to its target location"""

@staticmethod
def finalize():
"""Cleanup"""
def run_final(pathfmt, status):
"""Postprocessor finalization after all files have been downloaded"""

def __repr__(self):
return self.__class__.__name__
22 changes: 19 additions & 3 deletions gallery_dl/postprocessor/exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,43 @@ class ExecPP(PostProcessor):
def __init__(self, pathfmt, options):
PostProcessor.__init__(self)
args = options["command"]
final = options.get("final", False)

if isinstance(args, str):
if final:
self._format = self._format_args_directory
else:
self._format = self._format_args_path
if "{}" not in args:
args += " {}"
self.args = args
self.shell = True
self._format = self._format_args_string
else:
self._format = self._format_args_list
self.args = [util.Formatter(arg) for arg in args]
self.shell = False
self._format = self._format_args_list

if final:
self.run_after = PostProcessor.run_after
else:
self.run_final = PostProcessor.run_final

if options.get("async", False):
self._exec = self._exec_async

def run_after(self, pathfmt):
self._exec(self._format(pathfmt))

def _format_args_string(self, pathfmt):
def run_final(self, pathfmt, status):
if status == 0:
self._exec(self._format(pathfmt))

def _format_args_path(self, pathfmt):
return self.args.replace("{}", quote(pathfmt.realpath))

def _format_args_directory(self, pathfmt):
return self.args.replace("{}", quote(pathfmt.realdirectory))

def _format_args_list(self, pathfmt):
kwdict = pathfmt.kwdict
kwdict["_directory"] = pathfmt.realdirectory
Expand Down
2 changes: 1 addition & 1 deletion gallery_dl/postprocessor/zip.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def _write_safe(self, pathfmt):
with zipfile.ZipFile(*self.args) as zfile:
self._write(pathfmt, zfile)

def finalize(self):
def run_final(self, pathfmt, status):
if self.zfile:
self.zfile.close()

Expand Down
4 changes: 2 additions & 2 deletions test/test_postprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ def test_zip_write(self):
self.assertEqual(len(pp.zfile.NameToInfo), 3)

# close file
pp.finalize()
pp.run_final(self.pathfmt, 0)

# reopen to check persistence
with zipfile.ZipFile(pp.zfile.filename) as file:
Expand Down Expand Up @@ -360,7 +360,7 @@ def side_effect(_, name):
pp.prepare(self.pathfmt)
pp.run(self.pathfmt)

pp.finalize()
pp.run_final(self.pathfmt, 0)

self.assertEqual(pp.zfile.write.call_count, 3)
for call in pp.zfile.write.call_args_list:
Expand Down

0 comments on commit 9e88e7a

Please sign in to comment.