Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
mikf
GitHub Repository: mikf/gallery-dl
Path: blob/master/gallery_dl/actions.py
8905 views
1
# -*- coding: utf-8 -*-
2
3
# Copyright 2023-2025 Mike Fährmann
4
#
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License version 2 as
7
# published by the Free Software Foundation.
8
9
""" """
10
11
import time
12
import logging
13
import operator
14
import functools
15
from . import util, exception
16
17
18
def parse_logging(actionspec):
19
if isinstance(actionspec, dict):
20
actionspec = actionspec.items()
21
22
actions = {}
23
actions[-logging.DEBUG] = actions_bd = []
24
actions[-logging.INFO] = actions_bi = []
25
actions[-logging.WARNING] = actions_bw = []
26
actions[-logging.ERROR] = actions_be = []
27
actions[logging.DEBUG] = actions_ad = []
28
actions[logging.INFO] = actions_ai = []
29
actions[logging.WARNING] = actions_aw = []
30
actions[logging.ERROR] = actions_ae = []
31
32
for event, spec in actionspec:
33
level, _, pattern = event.partition(":")
34
search = util.re(pattern).search if pattern else util.true
35
36
if isinstance(spec, str):
37
type, _, args = spec.partition(" ")
38
before, after = ACTIONS[type](args)
39
else:
40
actions_before = []
41
actions_after = []
42
for s in spec:
43
type, _, args = s.partition(" ")
44
before, after = ACTIONS[type](args)
45
if before:
46
actions_before.append(before)
47
if after:
48
actions_after.append(after)
49
before = _chain_actions(actions_before)
50
after = _chain_actions(actions_after)
51
52
level = level.strip()
53
if not level or level == "*":
54
if before:
55
action = (search, before)
56
actions_bd.append(action)
57
actions_bi.append(action)
58
actions_bw.append(action)
59
actions_be.append(action)
60
if after:
61
action = (search, after)
62
actions_ad.append(action)
63
actions_ai.append(action)
64
actions_aw.append(action)
65
actions_ae.append(action)
66
else:
67
level = _level_to_int(level)
68
if before:
69
actions[-level].append((search, before))
70
if after:
71
actions[level].append((search, after))
72
73
return actions
74
75
76
def parse_signals(actionspec):
77
import signal
78
79
if isinstance(actionspec, dict):
80
actionspec = actionspec.items()
81
82
for signal_name, spec in actionspec:
83
signal_num = getattr(signal, signal_name, None)
84
if signal_num is None:
85
log = logging.getLogger("gallery-dl")
86
log.warning("signal '%s' is not defined", signal_name)
87
continue
88
89
if isinstance(spec, str):
90
type, _, args = spec.partition(" ")
91
before, after = ACTIONS[type](args)
92
action = before if after is None else after
93
else:
94
actions_before = []
95
actions_after = []
96
for s in spec:
97
type, _, args = s.partition(" ")
98
before, after = ACTIONS[type](args)
99
if before is not None:
100
actions_before.append(before)
101
if after is not None:
102
actions_after.append(after)
103
104
actions = actions_before
105
actions.extend(actions_after)
106
action = _chain_actions(actions)
107
108
signal.signal(signal_num, signals_handler(action))
109
110
111
class LoggerAdapter():
112
113
def __init__(self, logger, job):
114
self.logger = logger
115
self.extra = job._logger_extra
116
self.actions = job._logger_actions
117
118
self.debug = functools.partial(self.log, logging.DEBUG)
119
self.info = functools.partial(self.log, logging.INFO)
120
self.warning = functools.partial(self.log, logging.WARNING)
121
self.error = functools.partial(self.log, logging.ERROR)
122
123
def log(self, level, msg, *args, **kwargs):
124
msg = str(msg)
125
if args:
126
msg = msg % args
127
128
before = self.actions[-level]
129
after = self.actions[level]
130
131
if before:
132
args = self.extra.copy()
133
args["level"] = level
134
135
for cond, action in before:
136
if cond(msg):
137
action(args)
138
139
level = args["level"]
140
141
if self.logger.isEnabledFor(level):
142
kwargs["extra"] = self.extra
143
self.logger._log(level, msg, (), **kwargs)
144
145
if after:
146
args = self.extra.copy()
147
for cond, action in after:
148
if cond(msg):
149
action(args)
150
151
def traceback(self, exc):
152
if self.logger.isEnabledFor(logging.DEBUG):
153
self.logger._log(
154
logging.DEBUG, "", None, exc_info=exc, extra=self.extra)
155
156
157
def _level_to_int(level):
158
try:
159
return logging._nameToLevel[level]
160
except KeyError:
161
return int(level)
162
163
164
def _chain_actions(actions):
165
def _chain(args):
166
for action in actions:
167
action(args)
168
return _chain
169
170
171
def signals_handler(action, args={}):
172
def handler(signal_num, frame):
173
action(args)
174
return handler
175
176
177
# --------------------------------------------------------------------
178
179
def action_print(opts):
180
def _print(_):
181
print(opts)
182
return None, _print
183
184
185
def action_status(opts):
186
op, value = util.re(r"\s*([&|^=])=?\s*(\d+)").match(opts).groups()
187
188
op = {
189
"&": operator.and_,
190
"|": operator.or_,
191
"^": operator.xor,
192
"=": lambda x, y: y,
193
}[op]
194
195
value = int(value)
196
197
def _status(args):
198
args["job"].status = op(args["job"].status, value)
199
return _status, None
200
201
202
def action_level(opts):
203
level = _level_to_int(opts.lstrip(" ~="))
204
205
def _level(args):
206
args["level"] = level
207
return _level, None
208
209
210
def action_exec(opts):
211
def _exec(_):
212
util.Popen(opts, shell=True).wait()
213
return None, _exec
214
215
216
def action_wait(opts):
217
if opts:
218
seconds = util.build_duration_func(opts)
219
220
def _wait(args):
221
time.sleep(seconds())
222
else:
223
def _wait(args):
224
input("Press Enter to continue")
225
226
return None, _wait
227
228
229
def action_flag(opts):
230
flag, value = util.re(
231
r"(?i)(file|post|child|download)(?:\s*[= ]\s*(.+))?"
232
).match(opts).groups()
233
flag = flag.upper()
234
235
if value is None:
236
value = "stop"
237
elif value == "skip":
238
value = "stop" if flag == "DOWNLOAD" else False
239
else:
240
value = value.lower()
241
242
def _flag(args):
243
util.FLAGS.__dict__[flag] = value
244
return _flag, None
245
246
247
def action_raise(opts):
248
name, _, arg = opts.partition(" ")
249
250
exc = getattr(exception, name, None)
251
if exc is None:
252
import builtins
253
exc = getattr(builtins, name, Exception)
254
255
if arg:
256
def _raise(args):
257
raise exc(arg)
258
else:
259
def _raise(args):
260
raise exc()
261
262
return None, _raise
263
264
265
def action_abort(opts):
266
def _abort(_):
267
raise exception.StopExtraction(opts or None)
268
return None, _abort
269
270
271
def action_terminate(opts):
272
def _terminate(_):
273
raise exception.TerminateExtraction(opts)
274
return None, _terminate
275
276
277
def action_restart(opts):
278
def _restart(_):
279
raise exception.RestartExtraction(opts)
280
return None, _restart
281
282
283
def action_exit(opts):
284
try:
285
opts = int(opts)
286
except ValueError:
287
pass
288
289
def _exit(_):
290
raise SystemExit(opts)
291
return None, _exit
292
293
294
ACTIONS = {
295
"abort" : action_abort,
296
"exec" : action_exec,
297
"exit" : action_exit,
298
"flag" : action_flag,
299
"level" : action_level,
300
"print" : action_print,
301
"raise" : action_raise,
302
"restart" : action_restart,
303
"status" : action_status,
304
"terminate": action_terminate,
305
"wait" : action_wait,
306
}
307
308