jmcomic 2.6.6__py3-none-any.whl → 2.6.7__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 +1 -1
- jmcomic/jm_client_impl.py +2 -2
- jmcomic/jm_client_interface.py +8 -0
- jmcomic/jm_option.py +1 -1
- jmcomic/jm_plugin.py +71 -6
- jmcomic/jm_toolkit.py +26 -8
- {jmcomic-2.6.6.dist-info → jmcomic-2.6.7.dist-info}/METADATA +1 -1
- jmcomic-2.6.7.dist-info/RECORD +18 -0
- jmcomic-2.6.6.dist-info/RECORD +0 -18
- {jmcomic-2.6.6.dist-info → jmcomic-2.6.7.dist-info}/WHEEL +0 -0
- {jmcomic-2.6.6.dist-info → jmcomic-2.6.7.dist-info}/entry_points.txt +0 -0
- {jmcomic-2.6.6.dist-info → jmcomic-2.6.7.dist-info}/licenses/LICENSE +0 -0
- {jmcomic-2.6.6.dist-info → jmcomic-2.6.7.dist-info}/top_level.txt +0 -0
jmcomic/__init__.py
CHANGED
jmcomic/jm_client_impl.py
CHANGED
|
@@ -895,7 +895,7 @@ class JmApiClient(AbstractJmClient):
|
|
|
895
895
|
检查返回数据中的status字段是否为ok
|
|
896
896
|
"""
|
|
897
897
|
data = resp.model_data
|
|
898
|
-
if data.status
|
|
898
|
+
if data.status != 'ok':
|
|
899
899
|
ExceptionTool.raises_resp(data.msg, resp)
|
|
900
900
|
|
|
901
901
|
def req_api(self, url, get=True, require_success=True, **kwargs) -> JmApiResp:
|
|
@@ -995,7 +995,7 @@ class JmApiClient(AbstractJmClient):
|
|
|
995
995
|
# 找到第一个有效字符
|
|
996
996
|
ExceptionTool.require_true(
|
|
997
997
|
char == '{',
|
|
998
|
-
f'请求不是json格式,强制重试!响应文本: [{
|
|
998
|
+
f'请求不是json格式,强制重试!响应文本: [{JmcomicText.limit_text(text, 200)}]'
|
|
999
999
|
)
|
|
1000
1000
|
return resp
|
|
1001
1001
|
|
jmcomic/jm_client_interface.py
CHANGED
|
@@ -101,6 +101,14 @@ class JmApiResp(JmJsonResp):
|
|
|
101
101
|
super().__init__(resp)
|
|
102
102
|
self.ts = ts
|
|
103
103
|
|
|
104
|
+
# 重写json()方法,可以忽略一些非json格式的脏数据
|
|
105
|
+
@field_cache()
|
|
106
|
+
def json(self) -> Dict:
|
|
107
|
+
try:
|
|
108
|
+
return JmcomicText.try_parse_json_object(self.resp.text)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
ExceptionTool.raises_resp(f'json解析失败: {e}', self, JsonResolveFailException)
|
|
111
|
+
|
|
104
112
|
@property
|
|
105
113
|
def is_success(self) -> bool:
|
|
106
114
|
return super().is_success and self.json()['code'] == 200
|
jmcomic/jm_option.py
CHANGED
|
@@ -419,7 +419,7 @@ class JmOption:
|
|
|
419
419
|
if clazz == AbstractJmClient or not issubclass(clazz, AbstractJmClient):
|
|
420
420
|
raise NotImplementedError(clazz)
|
|
421
421
|
|
|
422
|
-
client:
|
|
422
|
+
client: JmcomicClient = clazz(
|
|
423
423
|
postman=postman,
|
|
424
424
|
domain_list=decide_domain_list(),
|
|
425
425
|
retry_times=retry_times,
|
jmcomic/jm_plugin.py
CHANGED
|
@@ -1221,23 +1221,88 @@ class ReplacePathStringPlugin(JmOptionPlugin):
|
|
|
1221
1221
|
class AdvancedRetryPlugin(JmOptionPlugin):
|
|
1222
1222
|
plugin_key = 'advanced-retry'
|
|
1223
1223
|
|
|
1224
|
+
def __init__(self, option: JmOption):
|
|
1225
|
+
super().__init__(option)
|
|
1226
|
+
self.retry_config = None
|
|
1227
|
+
|
|
1224
1228
|
def invoke(self,
|
|
1225
1229
|
retry_config,
|
|
1226
1230
|
**kwargs):
|
|
1231
|
+
self.require_param(isinstance(retry_config, dict), '必须配置retry_config为dict')
|
|
1232
|
+
self.retry_config = retry_config
|
|
1233
|
+
|
|
1227
1234
|
new_jm_client: Callable = self.option.new_jm_client
|
|
1228
1235
|
|
|
1229
1236
|
def hook_new_jm_client(*args, **kwargs):
|
|
1230
|
-
client:
|
|
1237
|
+
client: JmcomicClient = new_jm_client(*args, **kwargs)
|
|
1231
1238
|
client.domain_retry_strategy = self.request_with_retry
|
|
1239
|
+
client.domain_req_failed_counter = {}
|
|
1240
|
+
from threading import Lock
|
|
1241
|
+
client.domain_counter_lock = Lock()
|
|
1232
1242
|
return client
|
|
1233
1243
|
|
|
1234
1244
|
self.option.new_jm_client = hook_new_jm_client
|
|
1235
1245
|
|
|
1236
1246
|
def request_with_retry(self,
|
|
1237
|
-
client,
|
|
1238
|
-
request,
|
|
1239
|
-
url,
|
|
1240
|
-
is_image,
|
|
1247
|
+
client: AbstractJmClient,
|
|
1248
|
+
request: Callable,
|
|
1249
|
+
url: str,
|
|
1250
|
+
is_image: bool,
|
|
1241
1251
|
**kwargs,
|
|
1242
1252
|
):
|
|
1243
|
-
|
|
1253
|
+
"""
|
|
1254
|
+
实现如下域名重试机制:
|
|
1255
|
+
- 对域名列表轮询请求,配置:retry_rounds
|
|
1256
|
+
- 限制单个域名最大失败次数,配置:retry_domain_max_times
|
|
1257
|
+
- 轮询域名列表前,根据历史失败次数对域名列表排序,失败多的后置
|
|
1258
|
+
"""
|
|
1259
|
+
|
|
1260
|
+
def do_request(domain):
|
|
1261
|
+
url_to_use = url
|
|
1262
|
+
if url_to_use.startswith('/'):
|
|
1263
|
+
# path → url
|
|
1264
|
+
url_to_use = client.of_api_url(url, domain)
|
|
1265
|
+
client.update_request_with_specify_domain(kwargs, domain, is_image)
|
|
1266
|
+
jm_log(client.log_topic(), client.decode(url_to_use))
|
|
1267
|
+
elif is_image:
|
|
1268
|
+
# 图片url
|
|
1269
|
+
client.update_request_with_specify_domain(kwargs, None, is_image)
|
|
1270
|
+
|
|
1271
|
+
resp = request(url_to_use, **kwargs)
|
|
1272
|
+
resp = client.raise_if_resp_should_retry(resp, is_image)
|
|
1273
|
+
return resp
|
|
1274
|
+
|
|
1275
|
+
retry_domain_max_times: int = self.retry_config['retry_domain_max_times']
|
|
1276
|
+
retry_rounds: int = self.retry_config['retry_rounds']
|
|
1277
|
+
for rindex in range(retry_rounds):
|
|
1278
|
+
domain_list = self.get_sorted_domain(client, retry_domain_max_times)
|
|
1279
|
+
for i, domain in enumerate(domain_list):
|
|
1280
|
+
if self.failed_count(client, domain) >= retry_domain_max_times:
|
|
1281
|
+
continue
|
|
1282
|
+
|
|
1283
|
+
try:
|
|
1284
|
+
return do_request(domain)
|
|
1285
|
+
except Exception as e:
|
|
1286
|
+
from common import traceback_print_exec
|
|
1287
|
+
traceback_print_exec()
|
|
1288
|
+
jm_log('req.error', str(e))
|
|
1289
|
+
self.update_failed_count(client, domain)
|
|
1290
|
+
|
|
1291
|
+
return client.fallback(request, url, 0, 0, is_image, **kwargs)
|
|
1292
|
+
|
|
1293
|
+
def get_sorted_domain(self, client: JmcomicClient, times):
|
|
1294
|
+
domain_list = client.get_domain_list()
|
|
1295
|
+
return sorted(
|
|
1296
|
+
filter(lambda d: self.failed_count(client, d) < times, domain_list),
|
|
1297
|
+
key=lambda d: self.failed_count(client, d)
|
|
1298
|
+
)
|
|
1299
|
+
|
|
1300
|
+
# noinspection PyUnresolvedReferences
|
|
1301
|
+
def update_failed_count(self, client: AbstractJmClient, domain: str):
|
|
1302
|
+
with client.domain_counter_lock:
|
|
1303
|
+
client.domain_req_failed_counter[domain] = self.failed_count(client, domain) + 1
|
|
1304
|
+
|
|
1305
|
+
@staticmethod
|
|
1306
|
+
def failed_count(client: JmcomicClient, domain: str) -> int:
|
|
1307
|
+
# noinspection PyUnresolvedReferences
|
|
1308
|
+
return client.domain_req_failed_counter.get(domain, 0)
|
jmcomic/jm_toolkit.py
CHANGED
|
@@ -61,6 +61,8 @@ class JmcomicText:
|
|
|
61
61
|
|
|
62
62
|
# 提取接口返回值信息
|
|
63
63
|
pattern_ajax_favorite_msg = compile(r'</button>(.*?)</div>')
|
|
64
|
+
# 提取api接口返回值里的json,防止返回值里有无关日志导致json解析报错
|
|
65
|
+
pattern_api_response_json_object = compile(r'\{[\s\S]*?}')
|
|
64
66
|
|
|
65
67
|
@classmethod
|
|
66
68
|
def parse_to_jm_domain(cls, text: str):
|
|
@@ -344,6 +346,28 @@ class JmcomicText:
|
|
|
344
346
|
raise e
|
|
345
347
|
return save_dir
|
|
346
348
|
|
|
349
|
+
# noinspection PyTypeChecker
|
|
350
|
+
@classmethod
|
|
351
|
+
def try_parse_json_object(cls, resp_text: str) -> dict:
|
|
352
|
+
import json
|
|
353
|
+
text = resp_text.strip()
|
|
354
|
+
if text.startswith('{') and text.endswith('}'):
|
|
355
|
+
# fast case
|
|
356
|
+
return json.loads(text)
|
|
357
|
+
|
|
358
|
+
for match in cls.pattern_api_response_json_object.finditer(text):
|
|
359
|
+
try:
|
|
360
|
+
return json.loads(match.group(0))
|
|
361
|
+
except Exception as e:
|
|
362
|
+
jm_log('parse_json_object.error', e)
|
|
363
|
+
|
|
364
|
+
raise AssertionError(f'未解析出json数据: {cls.limit_text(resp_text, 200)}')
|
|
365
|
+
|
|
366
|
+
@classmethod
|
|
367
|
+
def limit_text(cls, text: str, limit: int) -> str:
|
|
368
|
+
length = len(text)
|
|
369
|
+
return text if length <= limit else (text[:limit] + f'...({length - limit}')
|
|
370
|
+
|
|
347
371
|
|
|
348
372
|
# 支持dsl: #{???} -> os.getenv(???)
|
|
349
373
|
JmcomicText.dsl_replacer.add_dsl_and_replacer(r'\$\{(.*?)\}', JmcomicText.match_os_env)
|
|
@@ -450,10 +474,7 @@ class JmPageTool:
|
|
|
450
474
|
# 这里不作解析,因为没什么用...
|
|
451
475
|
tags = cls.pattern_html_search_tags.findall(tag_text)
|
|
452
476
|
content.append((
|
|
453
|
-
album_id,
|
|
454
|
-
'name': title, # 改成name是为了兼容 parse_api_resp_to_page
|
|
455
|
-
'tags': tags
|
|
456
|
-
}
|
|
477
|
+
album_id, dict(name=title, tags=tags) # 改成name是为了兼容 parse_api_resp_to_page
|
|
457
478
|
))
|
|
458
479
|
|
|
459
480
|
return JmSearchPage(content, total)
|
|
@@ -468,10 +489,7 @@ class JmPageTool:
|
|
|
468
489
|
for (album_id, title, tag_text) in album_info_list:
|
|
469
490
|
tags = cls.pattern_html_search_tags.findall(tag_text)
|
|
470
491
|
content.append((
|
|
471
|
-
album_id,
|
|
472
|
-
'name': title, # 改成name是为了兼容 parse_api_resp_to_page
|
|
473
|
-
'tags': tags
|
|
474
|
-
}
|
|
492
|
+
album_id, dict(name=title, tags=tags) # 改成name是为了兼容 parse_api_resp_to_page
|
|
475
493
|
))
|
|
476
494
|
|
|
477
495
|
return JmSearchPage(content, total)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
jmcomic/__init__.py,sha256=Rg9DJ3YptC-BImHcbNB4cqM6-ZZMMtPGy2hw3-G7hZY,902
|
|
2
|
+
jmcomic/api.py,sha256=ZduhXDmh4lg1dkXHs7UTAaPpYNO7kcdPCDH5JJT9KSI,4253
|
|
3
|
+
jmcomic/cl.py,sha256=PBSh0JndNFZw3B7WJPj5Y8SeFdKzHE00jIwYo9An-K0,3475
|
|
4
|
+
jmcomic/jm_client_impl.py,sha256=sDrhqlO6zHAR0vlGFoKXmQ85l89BwksfG8BHGk7eXJ4,42165
|
|
5
|
+
jmcomic/jm_client_interface.py,sha256=4TDz-V8XLWPPxogLI-xiiHug4n6iir6UNeiKx9-KTXM,19410
|
|
6
|
+
jmcomic/jm_config.py,sha256=8GDETnzQObZabd441Boc--Mb--J2kr7HLxxViKB0DvI,17213
|
|
7
|
+
jmcomic/jm_downloader.py,sha256=azYkIN-1OuEivZ1TGGDaYo1FkcO5KatSb0g4GVdKSPY,11041
|
|
8
|
+
jmcomic/jm_entity.py,sha256=ZyXVy3mOl9qkxlw6hyshVS3RR-yKfO5BkJHtyBAoKQE,20053
|
|
9
|
+
jmcomic/jm_exception.py,sha256=x3KGMLlQS2zi1GX7z5G58zJN2EwLkI4mAURkxZYjEvA,5055
|
|
10
|
+
jmcomic/jm_option.py,sha256=T__PXJgzITdjyr2PMdk98jYMIKkUVGUA30p5lsMOTvQ,21244
|
|
11
|
+
jmcomic/jm_plugin.py,sha256=OekMjPVS25PMLcHV0r434BBvy0vbszlNiU5wODCZ3js,44524
|
|
12
|
+
jmcomic/jm_toolkit.py,sha256=9vwk4Q2gfh_04BhJxEoSsZcDT6ICh-wsN70WR4LSD9I,30511
|
|
13
|
+
jmcomic-2.6.7.dist-info/licenses/LICENSE,sha256=kz4coTxZxuGxisK3W00tjK57Zh3RcMGq-EnbXrK7-xA,1064
|
|
14
|
+
jmcomic-2.6.7.dist-info/METADATA,sha256=cuqBqo5Bp2GHItulju3coNt5dU9_UMtY1vcN0G0m0jk,9572
|
|
15
|
+
jmcomic-2.6.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
+
jmcomic-2.6.7.dist-info/entry_points.txt,sha256=tRbQltaGSBjejI0c9jYt-4SXQMd5nSDHcMvHmuTy4ow,44
|
|
17
|
+
jmcomic-2.6.7.dist-info/top_level.txt,sha256=puvVMFYJqIbd6NOTMEvOyugMTT8woBfSQyxEBan3zY4,8
|
|
18
|
+
jmcomic-2.6.7.dist-info/RECORD,,
|
jmcomic-2.6.6.dist-info/RECORD
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
jmcomic/__init__.py,sha256=jl9p808Fwi2NHktr1RmupkJuZ4GbRqTzExT2CouapZY,902
|
|
2
|
-
jmcomic/api.py,sha256=ZduhXDmh4lg1dkXHs7UTAaPpYNO7kcdPCDH5JJT9KSI,4253
|
|
3
|
-
jmcomic/cl.py,sha256=PBSh0JndNFZw3B7WJPj5Y8SeFdKzHE00jIwYo9An-K0,3475
|
|
4
|
-
jmcomic/jm_client_impl.py,sha256=atj557YHa0LDOezL9TSZrRA5lV9fs7CJzFLD1_MrONU,42141
|
|
5
|
-
jmcomic/jm_client_interface.py,sha256=Bld80cYwBwIqcMSJK09g5q8U_UX3IWnQJxuRJQwmIOk,19082
|
|
6
|
-
jmcomic/jm_config.py,sha256=8GDETnzQObZabd441Boc--Mb--J2kr7HLxxViKB0DvI,17213
|
|
7
|
-
jmcomic/jm_downloader.py,sha256=azYkIN-1OuEivZ1TGGDaYo1FkcO5KatSb0g4GVdKSPY,11041
|
|
8
|
-
jmcomic/jm_entity.py,sha256=ZyXVy3mOl9qkxlw6hyshVS3RR-yKfO5BkJHtyBAoKQE,20053
|
|
9
|
-
jmcomic/jm_exception.py,sha256=x3KGMLlQS2zi1GX7z5G58zJN2EwLkI4mAURkxZYjEvA,5055
|
|
10
|
-
jmcomic/jm_option.py,sha256=kErkn9-tXYp4h4lDQvc8jDYG1vqOMLIdIb3fbHvSsbA,21247
|
|
11
|
-
jmcomic/jm_plugin.py,sha256=0SnL6bc5kUeDoculz_YxlOdxFFg1B0EkAcDV1K0GIAk,41633
|
|
12
|
-
jmcomic/jm_toolkit.py,sha256=SIYCRZ6P4SPbAQA03U5z2DNiDldY7hWsAvKTdEzyK9U,29683
|
|
13
|
-
jmcomic-2.6.6.dist-info/licenses/LICENSE,sha256=kz4coTxZxuGxisK3W00tjK57Zh3RcMGq-EnbXrK7-xA,1064
|
|
14
|
-
jmcomic-2.6.6.dist-info/METADATA,sha256=bTugkFXmdS6nzfEk7hL6jCcweUBg0ntz3ksRaHJtcrM,9572
|
|
15
|
-
jmcomic-2.6.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
-
jmcomic-2.6.6.dist-info/entry_points.txt,sha256=tRbQltaGSBjejI0c9jYt-4SXQMd5nSDHcMvHmuTy4ow,44
|
|
17
|
-
jmcomic-2.6.6.dist-info/top_level.txt,sha256=puvVMFYJqIbd6NOTMEvOyugMTT8woBfSQyxEBan3zY4,8
|
|
18
|
-
jmcomic-2.6.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|