aboutsummaryrefslogtreecommitdiffstats
path: root/gallery_dl/postprocessor
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@unit193.net>2025-07-31 01:22:01 -0400
committerLibravatarUnit 193 <unit193@unit193.net>2025-07-31 01:22:01 -0400
commita6e995c093de8aae2e91a0787281bb34c0b871eb (patch)
tree2d79821b05300d34d8871eb6c9662b359a2de85d /gallery_dl/postprocessor
parent7672a750cb74bf31e21d76aad2776367fd476155 (diff)
New upstream version 1.30.2.upstream/1.30.2
Diffstat (limited to 'gallery_dl/postprocessor')
-rw-r--r--gallery_dl/postprocessor/__init__.py2
-rw-r--r--gallery_dl/postprocessor/common.py3
-rw-r--r--gallery_dl/postprocessor/compare.py11
-rw-r--r--gallery_dl/postprocessor/exec.py76
-rw-r--r--gallery_dl/postprocessor/metadata.py18
-rw-r--r--gallery_dl/postprocessor/mtime.py5
-rw-r--r--gallery_dl/postprocessor/ugoira.py53
7 files changed, 91 insertions, 77 deletions
diff --git a/gallery_dl/postprocessor/__init__.py b/gallery_dl/postprocessor/__init__.py
index dd44a8a..1a4ce56 100644
--- a/gallery_dl/postprocessor/__init__.py
+++ b/gallery_dl/postprocessor/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright 2018-2023 Mike Fährmann
+# Copyright 2018-2025 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
diff --git a/gallery_dl/postprocessor/common.py b/gallery_dl/postprocessor/common.py
index 3099547..8da8417 100644
--- a/gallery_dl/postprocessor/common.py
+++ b/gallery_dl/postprocessor/common.py
@@ -22,8 +22,7 @@ class PostProcessor():
return self.__class__.__name__
def _init_archive(self, job, options, prefix=None):
- archive_path = options.get("archive")
- if archive_path:
+ if archive_path := options.get("archive"):
extr = job.extractor
archive_table = options.get("archive-table")
diff --git a/gallery_dl/postprocessor/compare.py b/gallery_dl/postprocessor/compare.py
index c6bc54d..c3d328d 100644
--- a/gallery_dl/postprocessor/compare.py
+++ b/gallery_dl/postprocessor/compare.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright 2020-2023 Mike Fährmann
+# Copyright 2020-2025 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
@@ -21,8 +21,7 @@ class ComparePP(PostProcessor):
self._compare = self._compare_size
self._equal_exc = self._equal_cnt = 0
- equal = options.get("equal")
- if equal:
+ if equal := options.get("equal"):
equal, _, emax = equal.partition(":")
self._equal_max = text.parse_int(emax)
if equal == "abort":
@@ -62,12 +61,10 @@ class ComparePP(PostProcessor):
def _compare(self, f1, f2):
return self._compare_size(f1, f2) and self._compare_content(f1, f2)
- @staticmethod
- def _compare_size(f1, f2):
+ def _compare_size(self, f1, f2):
return os.stat(f1).st_size == os.stat(f2).st_size
- @staticmethod
- def _compare_content(f1, f2):
+ def _compare_content(self, f1, f2):
size = 16384
with open(f1, "rb") as fp1, open(f2, "rb") as fp2:
while True:
diff --git a/gallery_dl/postprocessor/exec.py b/gallery_dl/postprocessor/exec.py
index 7d2be2b..0bfe1a2 100644
--- a/gallery_dl/postprocessor/exec.py
+++ b/gallery_dl/postprocessor/exec.py
@@ -10,13 +10,14 @@
from .common import PostProcessor
from .. import util, formatter
+import subprocess
import os
-import re
if util.WINDOWS:
def quote(s):
- return '"' + s.replace('"', '\\"') + '"'
+ s = s.replace('"', '\\"')
+ return f'"{s}"'
else:
from shlex import quote
@@ -26,17 +27,21 @@ class ExecPP(PostProcessor):
def __init__(self, job, options):
PostProcessor.__init__(self, job)
- if options.get("async", False):
- self._exec = self._exec_async
-
- args = options["command"]
- if isinstance(args, str):
- self.args = args
- self._sub = re.compile(r"\{(_directory|_filename|_path|)\}").sub
- execute = self.exec_string
+ if cmds := options.get("commands"):
+ self.cmds = [self._prepare_cmd(c) for c in cmds]
+ execute = self.exec_many
else:
- self.args = [formatter.parse(arg) for arg in args]
- execute = self.exec_list
+ execute, self.args = self._prepare_cmd(options["command"])
+ if options.get("async", False):
+ self._exec = self._popen
+
+ self.session = False
+ self.creationflags = 0
+ if options.get("session"):
+ if util.WINDOWS:
+ self.creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
+ else:
+ self.session = True
events = options.get("event")
if events is None:
@@ -47,6 +52,13 @@ class ExecPP(PostProcessor):
self._init_archive(job, options)
+ def _prepare_cmd(self, cmd):
+ if isinstance(cmd, str):
+ self._sub = util.re(r"\{(_directory|_filename|_path|)\}").sub
+ return self.exec_string, cmd
+ else:
+ return self.exec_list, [formatter.parse(arg) for arg in cmd]
+
def exec_list(self, pathfmt):
archive = self.archive
kwdict = pathfmt.kwdict
@@ -60,10 +72,11 @@ class ExecPP(PostProcessor):
args = [arg.format_map(kwdict) for arg in self.args]
args[0] = os.path.expanduser(args[0])
- self._exec(args, False)
+ retcode = self._exec(args, False)
if archive:
archive.add(kwdict)
+ return retcode
def exec_string(self, pathfmt):
archive = self.archive
@@ -72,24 +85,47 @@ class ExecPP(PostProcessor):
self.pathfmt = pathfmt
args = self._sub(self._replace, self.args)
- self._exec(args, True)
+ retcode = self._exec(args, True)
if archive:
archive.add(pathfmt.kwdict)
+ return retcode
+
+ def exec_many(self, pathfmt):
+ if archive := self.archive:
+ if archive.check(pathfmt.kwdict):
+ return
+ self.archive = False
+
+ retcode = 0
+ for execute, args in self.cmds:
+ self.args = args
+ if retcode := execute(pathfmt):
+ # non-zero exit status
+ break
+
+ if archive:
+ self.archive = archive
+ archive.add(pathfmt.kwdict)
+ return retcode
def _exec(self, args, shell):
- self.log.debug("Running '%s'", args)
- retcode = util.Popen(args, shell=shell).wait()
- if retcode:
+ if retcode := self._popen(args, shell).wait():
self.log.warning("'%s' returned with non-zero exit status (%d)",
args, retcode)
+ return retcode
- def _exec_async(self, args, shell):
+ def _popen(self, args, shell):
self.log.debug("Running '%s'", args)
- util.Popen(args, shell=shell)
+ return util.Popen(
+ args,
+ shell=shell,
+ creationflags=self.creationflags,
+ start_new_session=self.session,
+ )
def _replace(self, match):
- name = match.group(1)
+ name = match[1]
if name == "_directory":
return quote(self.pathfmt.realdirectory)
if name == "_filename":
diff --git a/gallery_dl/postprocessor/metadata.py b/gallery_dl/postprocessor/metadata.py
index fbb3fb8..c74f92f 100644
--- a/gallery_dl/postprocessor/metadata.py
+++ b/gallery_dl/postprocessor/metadata.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright 2019-2023 Mike Fährmann
+# Copyright 2019-2025 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
@@ -55,8 +55,7 @@ class MetadataPP(PostProcessor):
self._json_encode = self._make_encoder(options, 4).encode
ext = "json"
- base_directory = options.get("base-directory")
- if base_directory:
+ if base_directory := options.get("base-directory"):
if base_directory is True:
self._base = lambda p: p.basedirectory
else:
@@ -139,9 +138,7 @@ class MetadataPP(PostProcessor):
archive.add(pathfmt.kwdict)
if self.mtime:
- mtime = pathfmt.kwdict.get("_mtime")
- if mtime:
- util.set_mtime(path, mtime)
+ pathfmt.set_mtime(path)
def _run_stdout(self, pathfmt):
self.write(sys.stdout, pathfmt.kwdict)
@@ -183,8 +180,7 @@ class MetadataPP(PostProcessor):
try:
pathfmt.directory_formatters = self._directory_formatters
pathfmt.directory_conditions = ()
- segments = pathfmt.build_directory(pathfmt.kwdict)
- if segments:
+ if segments := pathfmt.build_directory(pathfmt.kwdict):
directory = pathfmt.clean_path(os.sep.join(segments) + os.sep)
else:
directory = "." + os.sep
@@ -246,8 +242,7 @@ class MetadataPP(PostProcessor):
fp.write(self._json_encode(kwdict) + "\n")
def _make_filter(self, options):
- include = options.get("include")
- if include:
+ if include := options.get("include"):
if isinstance(include, str):
include = include.split(",")
return lambda d: {k: d[k] for k in include if k in d}
@@ -268,8 +263,7 @@ class MetadataPP(PostProcessor):
if not private:
return util.filter_dict
- @staticmethod
- def _make_encoder(options, indent=None):
+ def _make_encoder(self, options, indent=None):
return json.JSONEncoder(
ensure_ascii=options.get("ascii", False),
sort_keys=options.get("sort", False),
diff --git a/gallery_dl/postprocessor/mtime.py b/gallery_dl/postprocessor/mtime.py
index 6ded1e2..b1269dd 100644
--- a/gallery_dl/postprocessor/mtime.py
+++ b/gallery_dl/postprocessor/mtime.py
@@ -17,8 +17,7 @@ class MtimePP(PostProcessor):
def __init__(self, job, options):
PostProcessor.__init__(self, job)
- value = options.get("value")
- if value:
+ if value := options.get("value"):
self._get = formatter.parse(value, None, util.identity).format_map
else:
key = options.get("key", "date")
@@ -36,7 +35,7 @@ class MtimePP(PostProcessor):
if mtime is None:
return
- pathfmt.kwdict["_mtime"] = (
+ pathfmt.kwdict["_mtime_meta"] = (
util.datetime_to_timestamp(mtime)
if isinstance(mtime, datetime) else
text.parse_int(mtime)
diff --git a/gallery_dl/postprocessor/ugoira.py b/gallery_dl/postprocessor/ugoira.py
index 5340335..33ebb75 100644
--- a/gallery_dl/postprocessor/ugoira.py
+++ b/gallery_dl/postprocessor/ugoira.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright 2018-2023 Mike Fährmann
+# Copyright 2018-2025 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
@@ -170,8 +170,8 @@ class UgoiraPP(PostProcessor):
for frame in self._files:
# update frame filename extension
- frame["file"] = name = "{}.{}".format(
- frame["file"].partition(".")[0], frame["ext"])
+ frame["file"] = name = \
+ f"{frame['file'].partition('.')[0]}.{frame['ext']}"
if tempdir:
# move frame into tempdir
@@ -236,9 +236,7 @@ class UgoiraPP(PostProcessor):
pathfmt.realpath = pathfmt.temppath
else:
if self.mtime:
- mtime = pathfmt.kwdict.get("_mtime")
- if mtime:
- util.set_mtime(pathfmt.realpath, mtime)
+ pathfmt.set_mtime()
return True
def convert_to_archive(self, pathfmt, tempdir):
@@ -298,8 +296,7 @@ class UgoiraPP(PostProcessor):
def _exec(self, args):
self.log.debug(args)
out = None if self.output else subprocess.DEVNULL
- retcode = util.Popen(args, stdout=out, stderr=out).wait()
- if retcode:
+ if retcode := util.Popen(args, stdout=out, stderr=out).wait():
output.stderr_write("\n")
self.log.error("Non-zero exit status when running %s (%s)",
args, retcode)
@@ -334,7 +331,7 @@ class UgoiraPP(PostProcessor):
last_copy = last.copy()
frames.append(last_copy)
name, _, ext = last_copy["file"].rpartition(".")
- last_copy["file"] = "{:>06}.{}".format(int(name)+1, ext)
+ last_copy["file"] = f"{int(name) + 1:>06}.{ext}"
shutil.copyfile(tempdir + last["file"],
tempdir + last_copy["file"])
@@ -349,10 +346,8 @@ class UgoiraPP(PostProcessor):
"-f", "image2",
"-ts_from_file", "2",
"-pattern_type", "sequence",
- "-i", "{}%06d.{}".format(
- tempdir.replace("%", "%%"),
- frame["file"].rpartition(".")[2]
- ),
+ "-i", (f"{tempdir.replace('%', '%%')}%06d."
+ f"{frame['file'].rpartition('.')[2]}"),
]
def _process_mkvmerge(self, pathfmt, tempdir):
@@ -363,10 +358,8 @@ class UgoiraPP(PostProcessor):
self.ffmpeg,
"-f", "image2",
"-pattern_type", "sequence",
- "-i", "{}/%06d.{}".format(
- tempdir.replace("%", "%%"),
- self._frames[0]["file"].rpartition(".")[2]
- ),
+ "-i", (f"{tempdir.replace('%', '%%')}/%06d."
+ f"{self._frames[0]['file'].rpartition('.')[2]}"),
]
def _finalize_mkvmerge(self, pathfmt, tempdir):
@@ -384,14 +377,13 @@ class UgoiraPP(PostProcessor):
def _write_ffmpeg_concat(self, tempdir):
content = ["ffconcat version 1.0"]
- append = content.append
for frame in self._frames:
- append("file '{}'\nduration {}".format(
- frame["file"], frame["delay"] / 1000))
+ content.append(f"file '{frame['file']}'\n"
+ f"duration {frame['delay'] / 1000}")
if self.repeat:
- append("file '{}'".format(frame["file"]))
- append("")
+ content.append(f"file '{frame['file']}'")
+ content.append("")
ffconcat = tempdir + "/ffconcat.txt"
with open(ffconcat, "w") as fp:
@@ -400,14 +392,13 @@ class UgoiraPP(PostProcessor):
def _write_mkvmerge_timecodes(self, tempdir):
content = ["# timecode format v2"]
- append = content.append
delay_sum = 0
for frame in self._frames:
- append(str(delay_sum))
+ content.append(str(delay_sum))
delay_sum += frame["delay"]
- append(str(delay_sum))
- append("")
+ content.append(str(delay_sum))
+ content.append("")
timecodes = tempdir + "/timecodes.tc"
with open(timecodes, "w") as fp:
@@ -416,24 +407,22 @@ class UgoiraPP(PostProcessor):
def calculate_framerate(self, frames):
if self._delay_is_uniform(frames):
- return ("1000/{}".format(frames[0]["delay"]), None)
+ return (f"1000/{frames[0]['delay']}", None)
if not self.uniform:
gcd = self._delay_gcd(frames)
if gcd >= 10:
- return (None, "1000/{}".format(gcd))
+ return (None, f"1000/{gcd}")
return (None, None)
- @staticmethod
- def _delay_gcd(frames):
+ def _delay_gcd(self, frames):
result = frames[0]["delay"]
for f in frames:
result = gcd(result, f["delay"])
return result
- @staticmethod
- def _delay_is_uniform(frames):
+ def _delay_is_uniform(self, frames):
delay = frames[0]["delay"]
for f in frames:
if f["delay"] != delay: