initial 'signals-actions' implementation (#6582)

https://github.com/mikf/gallery-dl/issues/6582#issuecomment-2973285775

To stop gracefully after the current file finishes processing when
Ctrl+C was pressed, or after the current post finishes processing when
SIGUSR1 was received:

{
    "signals-actions": {
        "SIGINT" : "file",
        "SIGUSR1": "post"
    }
}
This commit is contained in:
Mike Fährmann
2025-07-09 22:22:32 +02:00
parent 4dfa8a75c0
commit 0210ffcdd8
3 changed files with 34 additions and 3 deletions

View File

@@ -11,7 +11,7 @@ import logging
from . import version, config, option, output, extractor, job, util, exception
__author__ = "Mike Fährmann"
__copyright__ = "Copyright 2014-2023 Mike Fährmann"
__copyright__ = "Copyright 2014-2025 Mike Fährmann"
__license__ = "GPLv2"
__maintainer__ = "Mike Fährmann"
__email__ = "mike_faehrmann@web.de"
@@ -78,8 +78,7 @@ def main():
output.configure_standard_streams()
# signals
signals = config.get((), "signals-ignore")
if signals:
if signals := config.get((), "signals-ignore"):
import signal
if isinstance(signals, str):
signals = signals.split(",")
@@ -90,6 +89,19 @@ def main():
else:
signal.signal(signal_num, signal.SIG_IGN)
if signals := config.get((), "signals-actions"):
import signal
for signal_name, action in signals.items():
signal_num = getattr(signal, signal_name, None)
if signal_num is None:
log.warning("signal '%s' is not defined", signal_name)
else:
def handler(signal_num, frame):
signal_name = signal.Signals(signal_num).name
output.stderr_write(f"{signal_name} received\n")
util.FLAGS.__dict__[action.upper()] = "stop"
signal.signal(signal_num, handler)
# enable ANSI escape sequences on Windows
if util.WINDOWS and config.get(("output",), "ansi", output.COLORS):
from ctypes import windll, wintypes, byref

View File

@@ -28,6 +28,7 @@ from . import (
)
from .extractor.message import Message
stdout_write = output.stdout_write
FLAGS = util.FLAGS
class Job():
@@ -200,6 +201,8 @@ class Job():
if self.pred_url(url, kwdict):
self.update_kwdict(kwdict)
self.handle_url(url, kwdict)
if FLAGS.FILE is not None:
FLAGS.FILE = FLAGS.process(FLAGS.FILE)
elif msg[0] == Message.Directory:
self.update_kwdict(msg[1])
@@ -212,6 +215,8 @@ class Job():
if self.pred_queue(url, kwdict):
self.update_kwdict(kwdict)
self.handle_queue(url, kwdict)
if FLAGS.CHILD is not None:
FLAGS.CHILD = FLAGS.process(FLAGS.CHILD)
def handle_url(self, url, kwdict):
"""Handle Message.Url"""
@@ -390,6 +395,8 @@ class DownloadJob(Job):
if "post-after" in self.hooks:
for callback in self.hooks["post-after"]:
callback(self.pathfmt)
if FLAGS.POST is not None:
FLAGS.POST = FLAGS.process(FLAGS.POST)
self.pathfmt.set_directory(kwdict)
if "post" in self.hooks:
for callback in self.hooks["post"]:

View File

@@ -722,6 +722,17 @@ class CustomNone():
__repr__ = __str__
class Flags():
def __init__(self):
self.FILE = self.POST = self.CHILD = self.DOWNLOAD = None
def process(self, flag):
if flag == "terminate":
raise exception.TerminateExtraction()
raise exception.StopExtraction()
# v137.0 release of Firefox on 2025-04-01 has ordinal 739342
# 735506 == 739342 - 137 * 28
# v135.0 release of Chrome on 2025-04-01 has ordinal 739342
@@ -737,6 +748,7 @@ re = text.re
re_compile = text.re_compile
NONE = CustomNone()
FLAGS = Flags()
EPOCH = datetime.datetime(1970, 1, 1)
SECOND = datetime.timedelta(0, 1)
WINDOWS = (os.name == "nt")