jmcomic 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jmcomic/__init__.py +29 -0
- jmcomic/api.py +131 -0
- jmcomic/cl.py +121 -0
- jmcomic/jm_client_impl.py +1217 -0
- jmcomic/jm_client_interface.py +609 -0
- jmcomic/jm_config.py +505 -0
- jmcomic/jm_downloader.py +350 -0
- jmcomic/jm_entity.py +695 -0
- jmcomic/jm_exception.py +191 -0
- jmcomic/jm_option.py +647 -0
- jmcomic/jm_plugin.py +1203 -0
- jmcomic/jm_toolkit.py +937 -0
- jmcomic-0.0.2.dist-info/METADATA +229 -0
- jmcomic-0.0.2.dist-info/RECORD +18 -0
- jmcomic-0.0.2.dist-info/WHEEL +5 -0
- jmcomic-0.0.2.dist-info/entry_points.txt +2 -0
- jmcomic-0.0.2.dist-info/licenses/LICENSE +21 -0
- jmcomic-0.0.2.dist-info/top_level.txt +1 -0
jmcomic/jm_config.py
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
from common import time_stamp, field_cache, ProxyBuilder
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def shuffled(lines):
|
|
5
|
+
from random import shuffle
|
|
6
|
+
from common import str_to_list
|
|
7
|
+
ls = str_to_list(lines)
|
|
8
|
+
shuffle(ls)
|
|
9
|
+
return ls
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def default_jm_logging(topic: str, msg: str):
|
|
13
|
+
from common import format_ts, current_thread
|
|
14
|
+
print('[{}] [{}]:【{}】{}'.format(format_ts(), current_thread().name, topic, msg))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# 禁漫常量
|
|
18
|
+
class JmMagicConstants:
|
|
19
|
+
# 搜索参数-排序
|
|
20
|
+
ORDER_BY_LATEST = 'mr'
|
|
21
|
+
ORDER_BY_VIEW = 'mv'
|
|
22
|
+
ORDER_BY_PICTURE = 'mp'
|
|
23
|
+
ORDER_BY_LIKE = 'tf'
|
|
24
|
+
|
|
25
|
+
ORDER_MONTH_RANKING = 'mv_m'
|
|
26
|
+
ORDER_WEEK_RANKING = 'mv_w'
|
|
27
|
+
ORDER_DAY_RANKING = 'mv_t'
|
|
28
|
+
|
|
29
|
+
# 搜索参数-时间段
|
|
30
|
+
TIME_TODAY = 't'
|
|
31
|
+
TIME_WEEK = 'w'
|
|
32
|
+
TIME_MONTH = 'm'
|
|
33
|
+
TIME_ALL = 'a'
|
|
34
|
+
|
|
35
|
+
# 分类参数API接口的category
|
|
36
|
+
CATEGORY_ALL = '0' # 全部
|
|
37
|
+
CATEGORY_DOUJIN = 'doujin' # 同人
|
|
38
|
+
CATEGORY_SINGLE = 'single' # 单本
|
|
39
|
+
CATEGORY_SHORT = 'short' # 短篇
|
|
40
|
+
CATEGORY_ANOTHER = 'another' # 其他
|
|
41
|
+
CATEGORY_HANMAN = 'hanman' # 韩漫
|
|
42
|
+
CATEGORY_MEIMAN = 'meiman' # 美漫
|
|
43
|
+
CATEGORY_DOUJIN_COSPLAY = 'doujin_cosplay' # cosplay
|
|
44
|
+
CATEGORY_3D = '3D' # 3D
|
|
45
|
+
CATEGORY_ENGLISH_SITE = 'english_site' # 英文站
|
|
46
|
+
|
|
47
|
+
# 副分类
|
|
48
|
+
SUB_CHINESE = 'chinese' # 汉化,通用副分类
|
|
49
|
+
SUB_JAPANESE = 'japanese' # 日语,通用副分类
|
|
50
|
+
|
|
51
|
+
# 其他类(CATEGORY_ANOTHER)的副分类
|
|
52
|
+
SUB_ANOTHER_OTHER = 'other' # 其他漫画
|
|
53
|
+
SUB_ANOTHER_3D = '3d' # 3D
|
|
54
|
+
SUB_ANOTHER_COSPLAY = 'cosplay' # cosplay
|
|
55
|
+
|
|
56
|
+
# 同人(SUB_CHINESE)的副分类
|
|
57
|
+
SUB_DOUJIN_CG = 'CG' # CG
|
|
58
|
+
SUB_DOUJIN_CHINESE = SUB_CHINESE
|
|
59
|
+
SUB_DOUJIN_JAPANESE = SUB_JAPANESE
|
|
60
|
+
|
|
61
|
+
# 短篇(CATEGORY_SHORT)的副分类
|
|
62
|
+
SUB_SHORT_CHINESE = SUB_CHINESE
|
|
63
|
+
SUB_SHORT_JAPANESE = SUB_JAPANESE
|
|
64
|
+
|
|
65
|
+
# 单本(CATEGORY_SINGLE)的副分类
|
|
66
|
+
SUB_SINGLE_CHINESE = SUB_CHINESE
|
|
67
|
+
SUB_SINGLE_JAPANESE = SUB_JAPANESE
|
|
68
|
+
SUB_SINGLE_YOUTH = 'youth'
|
|
69
|
+
|
|
70
|
+
# 图片分割参数
|
|
71
|
+
SCRAMBLE_220980 = 220980
|
|
72
|
+
SCRAMBLE_268850 = 268850
|
|
73
|
+
SCRAMBLE_421926 = 421926 # 2023-02-08后改了图片切割算法
|
|
74
|
+
|
|
75
|
+
# 移动端API密钥
|
|
76
|
+
APP_TOKEN_SECRET = '18comicAPP'
|
|
77
|
+
APP_TOKEN_SECRET_2 = '18comicAPPContent'
|
|
78
|
+
APP_DATA_SECRET = '185Hcomic3PAPP7R'
|
|
79
|
+
API_DOMAIN_SERVER_SECRET = 'diosfjckwpqpdfjkvnqQjsik'
|
|
80
|
+
APP_VERSION = '1.8.0'
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# 模块级别共用配置
|
|
84
|
+
class JmModuleConfig:
|
|
85
|
+
# 网站相关
|
|
86
|
+
PROT = "https://"
|
|
87
|
+
JM_REDIRECT_URL = f'{PROT}jm365.work/3YeBdF' # 永久網域,怕走失的小伙伴收藏起来
|
|
88
|
+
JM_PUB_URL = f'{PROT}jmcomic-fb.vip'
|
|
89
|
+
JM_CDN_IMAGE_URL_TEMPLATE = PROT + 'cdn-msp.{domain}/media/photos/{photo_id}/{index:05}{suffix}' # index 从1开始
|
|
90
|
+
JM_IMAGE_SUFFIX = ['.jpg', '.webp', '.png', '.gif']
|
|
91
|
+
|
|
92
|
+
# JM的异常网页内容
|
|
93
|
+
JM_ERROR_RESPONSE_TEXT = {
|
|
94
|
+
"Could not connect to mysql! Please check your database settings!": "禁漫服务器内部报错",
|
|
95
|
+
"Restricted Access!": "禁漫拒绝你所在ip地区的访问,你可以选择: 换域名/换代理",
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# JM的异常网页code
|
|
99
|
+
JM_ERROR_STATUS_CODE = {
|
|
100
|
+
403: 'ip地区禁止访问/爬虫被识别',
|
|
101
|
+
500: '500: 禁漫服务器内部异常(可能是服务器过载,可以切换ip或稍后重试)',
|
|
102
|
+
520: '520: Web server is returning an unknown error (禁漫服务器内部报错)',
|
|
103
|
+
524: '524: The origin web server timed out responding to this request. (禁漫服务器处理超时)',
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# 分页大小
|
|
107
|
+
PAGE_SIZE_SEARCH = 80
|
|
108
|
+
PAGE_SIZE_FAVORITE = 20
|
|
109
|
+
|
|
110
|
+
# 图片分隔相关
|
|
111
|
+
SCRAMBLE_CACHE = {}
|
|
112
|
+
|
|
113
|
+
# 当本子没有作者名字时,顶替作者名字
|
|
114
|
+
DEFAULT_AUTHOR = 'default_author'
|
|
115
|
+
|
|
116
|
+
# cookies,目前只在移动端使用,因为移动端请求接口须携带,但不会校验cookies的内容。
|
|
117
|
+
APP_COOKIES = None
|
|
118
|
+
|
|
119
|
+
# 移动端图片域名
|
|
120
|
+
DOMAIN_IMAGE_LIST = shuffled('''
|
|
121
|
+
cdn-msp.jmapiproxy1.cc
|
|
122
|
+
cdn-msp.jmapiproxy2.cc
|
|
123
|
+
cdn-msp2.jmapiproxy2.cc
|
|
124
|
+
cdn-msp3.jmapiproxy2.cc
|
|
125
|
+
cdn-msp.jmapinodeudzn.net
|
|
126
|
+
cdn-msp3.jmapinodeudzn.net
|
|
127
|
+
''')
|
|
128
|
+
|
|
129
|
+
# 移动端API域名
|
|
130
|
+
DOMAIN_API_LIST = shuffled('''
|
|
131
|
+
www.cdnmhwscc.vip
|
|
132
|
+
www.cdnplaystation6.club
|
|
133
|
+
www.cdnplaystation6.org
|
|
134
|
+
www.cdnuc.vip
|
|
135
|
+
www.cdn-mspjmapiproxy.xyz
|
|
136
|
+
''')
|
|
137
|
+
|
|
138
|
+
# 获取最新移动端API域名的地址
|
|
139
|
+
API_URL_DOMAIN_SERVER = f'{PROT}jmappc01-1308024008.cos.ap-guangzhou.myqcloud.com/server-2024.txt'
|
|
140
|
+
|
|
141
|
+
APP_HEADERS_TEMPLATE = {
|
|
142
|
+
'Accept-Encoding': 'gzip, deflate',
|
|
143
|
+
'user-agent': 'Mozilla/5.0 (Linux; Android 9; V1938CT Build/PQ3A.190705.11211812; wv) AppleWebKit/537.36 (KHTML, '
|
|
144
|
+
'like Gecko) Version/4.0 Chrome/91.0.4472.114 Safari/537.36',
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
APP_HEADERS_IMAGE = {
|
|
148
|
+
'Accept': 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
|
|
149
|
+
'X-Requested-With': 'com.jiaohua_browser',
|
|
150
|
+
'Referer': PROT + DOMAIN_API_LIST[0],
|
|
151
|
+
'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7',
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# 网页端headers
|
|
155
|
+
HTML_HEADERS_TEMPLATE = {
|
|
156
|
+
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,'
|
|
157
|
+
'application/signed-exchange;v=b3;q=0.7',
|
|
158
|
+
'accept-language': 'zh-CN,zh;q=0.9',
|
|
159
|
+
'cache-control': 'no-cache',
|
|
160
|
+
'dnt': '1',
|
|
161
|
+
'pragma': 'no-cache',
|
|
162
|
+
'priority': 'u=0, i',
|
|
163
|
+
'referer': 'https://18comic.vip/',
|
|
164
|
+
'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
|
|
165
|
+
'sec-ch-ua-mobile': '?0',
|
|
166
|
+
'sec-ch-ua-platform': '"Windows"',
|
|
167
|
+
'sec-fetch-dest': 'document',
|
|
168
|
+
'sec-fetch-mode': 'navigate',
|
|
169
|
+
'sec-fetch-site': 'none',
|
|
170
|
+
'sec-fetch-user': '?1',
|
|
171
|
+
'upgrade-insecure-requests': '1',
|
|
172
|
+
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 '
|
|
173
|
+
'Safari/537.36',
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# 网页端域名配置
|
|
177
|
+
# 无需配置,默认为None,需要的时候会发起请求获得
|
|
178
|
+
# 使用优先级:
|
|
179
|
+
# 1. DOMAIN_HTML_LIST
|
|
180
|
+
# 2. [DOMAIN_HTML]
|
|
181
|
+
DOMAIN_HTML = None
|
|
182
|
+
DOMAIN_HTML_LIST = None
|
|
183
|
+
|
|
184
|
+
# 模块级别的可重写类配置
|
|
185
|
+
CLASS_DOWNLOADER = None
|
|
186
|
+
CLASS_OPTION = None
|
|
187
|
+
CLASS_ALBUM = None
|
|
188
|
+
CLASS_PHOTO = None
|
|
189
|
+
CLASS_IMAGE = None
|
|
190
|
+
|
|
191
|
+
# 客户端注册表
|
|
192
|
+
REGISTRY_CLIENT = {}
|
|
193
|
+
# 插件注册表
|
|
194
|
+
REGISTRY_PLUGIN = {}
|
|
195
|
+
# 异常监听器
|
|
196
|
+
# key: 异常类
|
|
197
|
+
# value: 函数,参数只有异常对象,无需返回值
|
|
198
|
+
# 这个异常类(或者这个异常的子类)的实例将要被raise前,你的listener方法会被调用
|
|
199
|
+
REGISTRY_EXCEPTION_LISTENER = {}
|
|
200
|
+
|
|
201
|
+
# 执行log的函数
|
|
202
|
+
EXECUTOR_LOG = default_jm_logging
|
|
203
|
+
|
|
204
|
+
# 使用固定时间戳
|
|
205
|
+
FLAG_USE_FIX_TIMESTAMP = True
|
|
206
|
+
# 移动端Client初始化cookies
|
|
207
|
+
FLAG_API_CLIENT_REQUIRE_COOKIES = True
|
|
208
|
+
# 自动更新禁漫API域名
|
|
209
|
+
FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN = True
|
|
210
|
+
FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN_DONE = None
|
|
211
|
+
# log开关标记
|
|
212
|
+
FLAG_ENABLE_JM_LOG = True
|
|
213
|
+
# log时解码url
|
|
214
|
+
FLAG_DECODE_URL_WHEN_LOGGING = True
|
|
215
|
+
# 当内置的版本号落后时,使用最新的禁漫app版本号
|
|
216
|
+
FLAG_USE_VERSION_NEWER_IF_BEHIND = True
|
|
217
|
+
|
|
218
|
+
# 关联dir_rule的自定义字段与对应的处理函数
|
|
219
|
+
# 例如:
|
|
220
|
+
# Amyname -> JmModuleConfig.AFIELD_ADVICE['myname'] = lambda album: "自定义名称"
|
|
221
|
+
AFIELD_ADVICE = dict()
|
|
222
|
+
PFIELD_ADVICE = dict()
|
|
223
|
+
|
|
224
|
+
# 当发生 oserror: [Errno 36] File name too long 时,
|
|
225
|
+
# 把文件名限制在指定个字符以内
|
|
226
|
+
VAR_FILE_NAME_LENGTH_LIMIT = 100
|
|
227
|
+
|
|
228
|
+
@classmethod
|
|
229
|
+
def downloader_class(cls):
|
|
230
|
+
if cls.CLASS_DOWNLOADER is not None:
|
|
231
|
+
return cls.CLASS_DOWNLOADER
|
|
232
|
+
|
|
233
|
+
from .jm_downloader import JmDownloader
|
|
234
|
+
return JmDownloader
|
|
235
|
+
|
|
236
|
+
@classmethod
|
|
237
|
+
def option_class(cls):
|
|
238
|
+
if cls.CLASS_OPTION is not None:
|
|
239
|
+
return cls.CLASS_OPTION
|
|
240
|
+
|
|
241
|
+
from .jm_option import JmOption
|
|
242
|
+
return JmOption
|
|
243
|
+
|
|
244
|
+
@classmethod
|
|
245
|
+
def album_class(cls):
|
|
246
|
+
if cls.CLASS_ALBUM is not None:
|
|
247
|
+
return cls.CLASS_ALBUM
|
|
248
|
+
|
|
249
|
+
from .jm_entity import JmAlbumDetail
|
|
250
|
+
return JmAlbumDetail
|
|
251
|
+
|
|
252
|
+
@classmethod
|
|
253
|
+
def photo_class(cls):
|
|
254
|
+
if cls.CLASS_PHOTO is not None:
|
|
255
|
+
return cls.CLASS_PHOTO
|
|
256
|
+
|
|
257
|
+
from .jm_entity import JmPhotoDetail
|
|
258
|
+
return JmPhotoDetail
|
|
259
|
+
|
|
260
|
+
@classmethod
|
|
261
|
+
def image_class(cls):
|
|
262
|
+
if cls.CLASS_IMAGE is not None:
|
|
263
|
+
return cls.CLASS_IMAGE
|
|
264
|
+
|
|
265
|
+
from .jm_entity import JmImageDetail
|
|
266
|
+
return JmImageDetail
|
|
267
|
+
|
|
268
|
+
@classmethod
|
|
269
|
+
def client_impl_class(cls, client_key: str):
|
|
270
|
+
clazz_dict = cls.REGISTRY_CLIENT
|
|
271
|
+
|
|
272
|
+
clazz = clazz_dict.get(client_key, None)
|
|
273
|
+
if clazz is None:
|
|
274
|
+
from .jm_toolkit import ExceptionTool
|
|
275
|
+
ExceptionTool.raises(f'not found client impl class for key: "{client_key}"')
|
|
276
|
+
|
|
277
|
+
return clazz
|
|
278
|
+
|
|
279
|
+
@classmethod
|
|
280
|
+
@field_cache("DOMAIN_HTML")
|
|
281
|
+
def get_html_domain(cls, postman=None):
|
|
282
|
+
"""
|
|
283
|
+
由于禁漫的域名经常变化,调用此方法可以获取一个当前可用的最新的域名 domain,
|
|
284
|
+
并且设置把 domain 设置为禁漫模块的默认域名。
|
|
285
|
+
这样一来,配置文件也不用配置域名了,一切都在运行时动态获取。
|
|
286
|
+
"""
|
|
287
|
+
from .jm_toolkit import JmcomicText
|
|
288
|
+
return JmcomicText.parse_to_jm_domain(cls.get_html_url(postman))
|
|
289
|
+
|
|
290
|
+
@classmethod
|
|
291
|
+
def get_html_url(cls, postman=None):
|
|
292
|
+
"""
|
|
293
|
+
访问禁漫的永久网域,从而得到一个可用的禁漫网址
|
|
294
|
+
:returns: https://jm-comic2.cc
|
|
295
|
+
"""
|
|
296
|
+
postman = postman or cls.new_postman(session=True)
|
|
297
|
+
|
|
298
|
+
url = postman.with_redirect_catching().get(cls.JM_REDIRECT_URL)
|
|
299
|
+
cls.jm_log('module.html_url', f'获取禁漫网页URL: [{cls.JM_REDIRECT_URL}] → [{url}]')
|
|
300
|
+
return url
|
|
301
|
+
|
|
302
|
+
@classmethod
|
|
303
|
+
@field_cache("DOMAIN_HTML_LIST")
|
|
304
|
+
def get_html_domain_all(cls, postman=None):
|
|
305
|
+
"""
|
|
306
|
+
访问禁漫发布页,得到所有的禁漫网页域名
|
|
307
|
+
|
|
308
|
+
:returns: ['18comic.vip', ..., 'jm365.xyz/ZNPJam'], 最后一个是【APP軟件下載】
|
|
309
|
+
"""
|
|
310
|
+
postman = postman or cls.new_postman(session=True)
|
|
311
|
+
|
|
312
|
+
resp = postman.get(cls.JM_PUB_URL)
|
|
313
|
+
if resp.status_code != 200:
|
|
314
|
+
from .jm_toolkit import ExceptionTool
|
|
315
|
+
ExceptionTool.raises_resp(f'请求失败,访问禁漫发布页获取所有域名,HTTP状态码为: {resp.status_code}', resp)
|
|
316
|
+
|
|
317
|
+
from .jm_toolkit import JmcomicText
|
|
318
|
+
domain_list = JmcomicText.analyse_jm_pub_html(resp.text)
|
|
319
|
+
|
|
320
|
+
cls.jm_log('module.html_domain_all', f'获取禁漫网页全部域名: [{resp.url}] → {domain_list}')
|
|
321
|
+
return domain_list
|
|
322
|
+
|
|
323
|
+
@classmethod
|
|
324
|
+
def get_html_domain_all_via_github(cls,
|
|
325
|
+
postman=None,
|
|
326
|
+
template='https://jmcmomic.github.io/go/{}.html',
|
|
327
|
+
index_range=(300, 309)
|
|
328
|
+
):
|
|
329
|
+
"""
|
|
330
|
+
通过禁漫官方的github号的repo获取最新的禁漫域名
|
|
331
|
+
https://github.com/jmcmomic/jmcmomic.github.io
|
|
332
|
+
"""
|
|
333
|
+
postman = postman or cls.new_postman(headers={
|
|
334
|
+
'authority': 'github.com',
|
|
335
|
+
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 '
|
|
336
|
+
'Safari/537.36'
|
|
337
|
+
})
|
|
338
|
+
domain_set = set()
|
|
339
|
+
|
|
340
|
+
def fetch_domain(url):
|
|
341
|
+
resp = postman.get(url, allow_redirects=False)
|
|
342
|
+
text = resp.text
|
|
343
|
+
from .jm_toolkit import JmcomicText
|
|
344
|
+
for domain in JmcomicText.analyse_jm_pub_html(text):
|
|
345
|
+
if domain.startswith('jm365'):
|
|
346
|
+
continue
|
|
347
|
+
domain_set.add(domain)
|
|
348
|
+
|
|
349
|
+
from common import multi_thread_launcher
|
|
350
|
+
|
|
351
|
+
multi_thread_launcher(
|
|
352
|
+
iter_objs=[template.format(i) for i in range(*index_range)],
|
|
353
|
+
apply_each_obj_func=fetch_domain,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
return domain_set
|
|
357
|
+
|
|
358
|
+
@classmethod
|
|
359
|
+
def new_html_headers(cls, domain='18comic.vip'):
|
|
360
|
+
"""
|
|
361
|
+
网页端的headers
|
|
362
|
+
"""
|
|
363
|
+
headers = cls.HTML_HEADERS_TEMPLATE.copy()
|
|
364
|
+
headers.update({
|
|
365
|
+
'authority': domain,
|
|
366
|
+
'origin': f'https://{domain}',
|
|
367
|
+
'referer': f'https://{domain}',
|
|
368
|
+
})
|
|
369
|
+
return headers
|
|
370
|
+
|
|
371
|
+
@classmethod
|
|
372
|
+
@field_cache()
|
|
373
|
+
def get_fix_ts_token_tokenparam(cls):
|
|
374
|
+
ts = time_stamp()
|
|
375
|
+
from .jm_toolkit import JmCryptoTool
|
|
376
|
+
token, tokenparam = JmCryptoTool.token_and_tokenparam(ts)
|
|
377
|
+
return ts, token, tokenparam
|
|
378
|
+
|
|
379
|
+
# noinspection PyUnusedLocal
|
|
380
|
+
@classmethod
|
|
381
|
+
def jm_log(cls, topic: str, msg: str):
|
|
382
|
+
if cls.FLAG_ENABLE_JM_LOG is True:
|
|
383
|
+
cls.EXECUTOR_LOG(topic, msg)
|
|
384
|
+
|
|
385
|
+
@classmethod
|
|
386
|
+
def disable_jm_log(cls):
|
|
387
|
+
cls.FLAG_ENABLE_JM_LOG = False
|
|
388
|
+
|
|
389
|
+
@classmethod
|
|
390
|
+
def new_postman(cls, session=False, **kwargs):
|
|
391
|
+
kwargs.setdefault('impersonate', 'chrome')
|
|
392
|
+
kwargs.setdefault('headers', JmModuleConfig.new_html_headers())
|
|
393
|
+
kwargs.setdefault('proxies', JmModuleConfig.DEFAULT_PROXIES)
|
|
394
|
+
|
|
395
|
+
from common import Postmans
|
|
396
|
+
|
|
397
|
+
if session is True:
|
|
398
|
+
return Postmans.new_session(**kwargs)
|
|
399
|
+
|
|
400
|
+
return Postmans.new_postman(**kwargs)
|
|
401
|
+
|
|
402
|
+
# option 相关的默认配置
|
|
403
|
+
# 一般情况下,建议使用option配置文件来定制配置
|
|
404
|
+
# 而如果只想修改几个简单常用的配置,也可以下方的DEFAULT_XXX属性
|
|
405
|
+
JM_OPTION_VER = '2.1'
|
|
406
|
+
DEFAULT_CLIENT_IMPL = 'api' # 默认Client实现类型为网页端
|
|
407
|
+
DEFAULT_CLIENT_CACHE = None # 默认关闭Client缓存。缓存的配置详见 CacheRegistry
|
|
408
|
+
DEFAULT_PROXIES = ProxyBuilder.system_proxy() # 默认使用系统代理
|
|
409
|
+
|
|
410
|
+
DEFAULT_OPTION_DICT: dict = {
|
|
411
|
+
'log': None,
|
|
412
|
+
'dir_rule': {'rule': 'Bd_Pname', 'base_dir': None},
|
|
413
|
+
'download': {
|
|
414
|
+
'cache': True,
|
|
415
|
+
'image': {'decode': True, 'suffix': None},
|
|
416
|
+
'threading': {
|
|
417
|
+
'image': 30,
|
|
418
|
+
'photo': None,
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
'client': {
|
|
422
|
+
'cache': None, # see CacheRegistry
|
|
423
|
+
'domain': [],
|
|
424
|
+
'postman': {
|
|
425
|
+
'type': 'curl_cffi',
|
|
426
|
+
'meta_data': {
|
|
427
|
+
'impersonate': 'chrome',
|
|
428
|
+
'headers': None,
|
|
429
|
+
'proxies': None,
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
'impl': None,
|
|
433
|
+
'retry_times': 5,
|
|
434
|
+
},
|
|
435
|
+
'plugins': {
|
|
436
|
+
# 如果插件抛出参数校验异常,只log。(全局配置,可以被插件的局部配置覆盖)
|
|
437
|
+
# 可选值:ignore(忽略),log(打印日志),raise(抛异常)。
|
|
438
|
+
'valid': 'log',
|
|
439
|
+
},
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
@classmethod
|
|
443
|
+
def option_default_dict(cls) -> dict:
|
|
444
|
+
"""
|
|
445
|
+
返回JmOption.default()的默认配置字典。
|
|
446
|
+
这样做是为了支持外界自行覆盖option默认配置字典
|
|
447
|
+
"""
|
|
448
|
+
from copy import deepcopy
|
|
449
|
+
|
|
450
|
+
option_dict = deepcopy(cls.DEFAULT_OPTION_DICT)
|
|
451
|
+
|
|
452
|
+
# log
|
|
453
|
+
if option_dict['log'] is None:
|
|
454
|
+
option_dict['log'] = cls.FLAG_ENABLE_JM_LOG
|
|
455
|
+
|
|
456
|
+
# dir_rule.base_dir
|
|
457
|
+
dir_rule = option_dict['dir_rule']
|
|
458
|
+
if dir_rule['base_dir'] is None:
|
|
459
|
+
import os
|
|
460
|
+
dir_rule['base_dir'] = os.getcwd()
|
|
461
|
+
|
|
462
|
+
# client cache
|
|
463
|
+
client = option_dict['client']
|
|
464
|
+
if client['cache'] is None:
|
|
465
|
+
client['cache'] = cls.DEFAULT_CLIENT_CACHE
|
|
466
|
+
|
|
467
|
+
# client impl
|
|
468
|
+
if client['impl'] is None:
|
|
469
|
+
client['impl'] = cls.DEFAULT_CLIENT_IMPL
|
|
470
|
+
|
|
471
|
+
# postman proxies
|
|
472
|
+
meta_data = client['postman']['meta_data']
|
|
473
|
+
if meta_data['proxies'] is None:
|
|
474
|
+
# use system proxy by default
|
|
475
|
+
meta_data['proxies'] = cls.DEFAULT_PROXIES
|
|
476
|
+
|
|
477
|
+
# threading photo
|
|
478
|
+
dt = option_dict['download']['threading']
|
|
479
|
+
if dt['photo'] is None:
|
|
480
|
+
import os
|
|
481
|
+
dt['photo'] = os.cpu_count()
|
|
482
|
+
|
|
483
|
+
return option_dict
|
|
484
|
+
|
|
485
|
+
@classmethod
|
|
486
|
+
def register_plugin(cls, plugin_class):
|
|
487
|
+
from .jm_toolkit import ExceptionTool
|
|
488
|
+
ExceptionTool.require_true(getattr(plugin_class, 'plugin_key', None) is not None,
|
|
489
|
+
f'未配置plugin_key, class: {plugin_class}')
|
|
490
|
+
cls.REGISTRY_PLUGIN[plugin_class.plugin_key] = plugin_class
|
|
491
|
+
|
|
492
|
+
@classmethod
|
|
493
|
+
def register_client(cls, client_class):
|
|
494
|
+
from .jm_toolkit import ExceptionTool
|
|
495
|
+
ExceptionTool.require_true(getattr(client_class, 'client_key', None) is not None,
|
|
496
|
+
f'未配置client_key, class: {client_class}')
|
|
497
|
+
cls.REGISTRY_CLIENT[client_class.client_key] = client_class
|
|
498
|
+
|
|
499
|
+
@classmethod
|
|
500
|
+
def register_exception_listener(cls, etype, listener):
|
|
501
|
+
cls.REGISTRY_EXCEPTION_LISTENER[etype] = listener
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
jm_log = JmModuleConfig.jm_log
|
|
505
|
+
disable_jm_log = JmModuleConfig.disable_jm_log
|