Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
mikf
GitHub Repository: mikf/gallery-dl
Path: blob/master/gallery_dl/downloader/common.py
8935 views
1
# -*- coding: utf-8 -*-
2
3
# Copyright 2014-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
"""Common classes and constants used by downloader modules."""
10
11
import os
12
from .. import config, util
13
_config = config._config
14
15
16
class DownloaderBase():
17
"""Base class for downloaders"""
18
scheme = ""
19
20
def __init__(self, job):
21
extractor = job.extractor
22
self.log = job.get_logger("downloader." + self.scheme)
23
24
if opts := self._extractor_config(extractor):
25
self.opts = opts
26
self.config = self.config_opts
27
28
self.out = job.out
29
self.session = extractor.session
30
self.part = self.config("part", True)
31
self.partdir = self.config("part-directory")
32
33
if self.partdir:
34
if isinstance(self.partdir, dict):
35
self.partdir = [
36
(util.compile_filter(expr) if expr else util.true,
37
util.expand_path(pdir))
38
for expr, pdir in self.partdir.items()
39
]
40
else:
41
self.partdir = util.expand_path(self.partdir)
42
os.makedirs(self.partdir, exist_ok=True)
43
44
proxies = self.config("proxy", util.SENTINEL)
45
if proxies is util.SENTINEL:
46
self.proxies = extractor._proxies
47
else:
48
self.proxies = util.build_proxy_map(proxies, self.log)
49
50
def config(self, key, default=None):
51
"""Interpolate downloader config value for 'key'"""
52
return config.interpolate(("downloader", self.scheme), key, default)
53
54
def config_opts(self, key, default=None, conf=_config):
55
if key in conf:
56
return conf[key]
57
value = self.opts.get(key, util.SENTINEL)
58
if value is not util.SENTINEL:
59
return value
60
return config.interpolate(("downloader", self.scheme), key, default)
61
62
def _extractor_config(self, extractor):
63
path = extractor._cfgpath
64
if not isinstance(path, list):
65
return self._extractor_opts(path[1], path[2])
66
67
opts = {}
68
for cat, sub in reversed(path):
69
if popts := self._extractor_opts(cat, sub):
70
opts.update(popts)
71
return opts
72
73
def _extractor_opts(self, category, subcategory):
74
cfg = config.get(("extractor",), category)
75
if not cfg:
76
return None
77
78
if copts := cfg.get(self.scheme):
79
if subcategory in cfg:
80
try:
81
if sopts := cfg[subcategory].get(self.scheme):
82
opts = copts.copy()
83
opts.update(sopts)
84
return opts
85
except Exception:
86
self._report_config_error(subcategory, cfg[subcategory])
87
return copts
88
89
if subcategory in cfg:
90
try:
91
return cfg[subcategory].get(self.scheme)
92
except Exception:
93
self._report_config_error(subcategory, cfg[subcategory])
94
95
return None
96
97
def _report_config_error(self, subcategory, value):
98
config.log.warning("Subcategory '%s' set to '%s' instead of object",
99
subcategory, util.json_dumps(value).strip('"'))
100
101
def download(self, url, pathfmt):
102
"""Write data from 'url' into the file specified by 'pathfmt'"""
103
104