jmcomic 2.6.5__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 +71 -61
- jmcomic/jm_client_interface.py +8 -0
- jmcomic/jm_config.py +5 -2
- jmcomic/jm_option.py +1 -1
- jmcomic/jm_plugin.py +90 -0
- jmcomic/jm_toolkit.py +26 -8
- {jmcomic-2.6.5.dist-info → jmcomic-2.6.7.dist-info}/METADATA +1 -1
- jmcomic-2.6.7.dist-info/RECORD +18 -0
- jmcomic-2.6.5.dist-info/RECORD +0 -18
- {jmcomic-2.6.5.dist-info → jmcomic-2.6.7.dist-info}/WHEEL +0 -0
- {jmcomic-2.6.5.dist-info → jmcomic-2.6.7.dist-info}/entry_points.txt +0 -0
- {jmcomic-2.6.5.dist-info → jmcomic-2.6.7.dist-info}/licenses/LICENSE +0 -0
- {jmcomic-2.6.5.dist-info → jmcomic-2.6.7.dist-info}/top_level.txt +0 -0
jmcomic/__init__.py
CHANGED
jmcomic/jm_client_impl.py
CHANGED
|
@@ -26,6 +26,7 @@ class AbstractJmClient(
|
|
|
26
26
|
super().__init__(postman)
|
|
27
27
|
self.retry_times = retry_times
|
|
28
28
|
self.domain_list = domain_list
|
|
29
|
+
self.domain_retry_strategy = None
|
|
29
30
|
self.CLIENT_CACHE = None
|
|
30
31
|
self._username = None # help for favorite_folder method
|
|
31
32
|
self.enable_cache()
|
|
@@ -44,23 +45,14 @@ class AbstractJmClient(
|
|
|
44
45
|
return JmcomicText.format_url(api_path, domain)
|
|
45
46
|
|
|
46
47
|
def get_jm_image(self, img_url) -> JmImageResp:
|
|
47
|
-
|
|
48
|
-
def callback(resp):
|
|
49
|
-
"""
|
|
50
|
-
使用此方法包装 self.get,使得图片数据为空时,判定为请求失败时,走重试逻辑
|
|
51
|
-
"""
|
|
52
|
-
resp = JmImageResp(resp)
|
|
53
|
-
resp.require_success()
|
|
54
|
-
return resp
|
|
55
|
-
|
|
56
|
-
return self.get(img_url, callback=callback, headers=JmModuleConfig.new_html_headers())
|
|
48
|
+
return self.get(img_url, is_image=True, headers=JmModuleConfig.new_html_headers())
|
|
57
49
|
|
|
58
50
|
def request_with_retry(self,
|
|
59
51
|
request,
|
|
60
52
|
url,
|
|
61
53
|
domain_index=0,
|
|
62
54
|
retry_count=0,
|
|
63
|
-
|
|
55
|
+
is_image=False,
|
|
64
56
|
**kwargs,
|
|
65
57
|
):
|
|
66
58
|
"""
|
|
@@ -74,11 +66,19 @@ class AbstractJmClient(
|
|
|
74
66
|
:param url: 图片url / path (/album/xxx)
|
|
75
67
|
:param domain_index: 域名下标
|
|
76
68
|
:param retry_count: 重试次数
|
|
77
|
-
:param
|
|
69
|
+
:param is_image: 是否是图片请求
|
|
78
70
|
:param kwargs: 请求方法的kwargs
|
|
79
71
|
"""
|
|
72
|
+
if self.domain_retry_strategy:
|
|
73
|
+
return self.domain_retry_strategy(self,
|
|
74
|
+
request,
|
|
75
|
+
url,
|
|
76
|
+
is_image,
|
|
77
|
+
**kwargs,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
80
|
if domain_index >= len(self.domain_list):
|
|
81
|
-
return self.fallback(request, url, domain_index, retry_count, **kwargs)
|
|
81
|
+
return self.fallback(request, url, domain_index, retry_count, is_image, **kwargs)
|
|
82
82
|
|
|
83
83
|
url_backup = url
|
|
84
84
|
|
|
@@ -87,12 +87,12 @@ class AbstractJmClient(
|
|
|
87
87
|
domain = self.domain_list[domain_index]
|
|
88
88
|
url = self.of_api_url(url, domain)
|
|
89
89
|
|
|
90
|
-
self.update_request_with_specify_domain(kwargs, domain)
|
|
90
|
+
self.update_request_with_specify_domain(kwargs, domain, is_image)
|
|
91
91
|
|
|
92
92
|
jm_log(self.log_topic(), self.decode(url))
|
|
93
|
-
|
|
93
|
+
elif is_image:
|
|
94
94
|
# 图片url
|
|
95
|
-
self.update_request_with_specify_domain(kwargs, None,
|
|
95
|
+
self.update_request_with_specify_domain(kwargs, None, is_image)
|
|
96
96
|
|
|
97
97
|
if domain_index != 0 or retry_count != 0:
|
|
98
98
|
jm_log(f'req.retry',
|
|
@@ -106,14 +106,8 @@ class AbstractJmClient(
|
|
|
106
106
|
|
|
107
107
|
try:
|
|
108
108
|
resp = request(url, **kwargs)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if callback is not None:
|
|
112
|
-
resp = callback(resp)
|
|
113
|
-
|
|
114
|
-
# 依然是回调,在最后返回之前,还可以判断resp是否重试
|
|
115
|
-
resp = self.raise_if_resp_should_retry(resp)
|
|
116
|
-
|
|
109
|
+
# 在最后返回之前,还可以判断resp是否重试
|
|
110
|
+
resp = self.raise_if_resp_should_retry(resp, is_image)
|
|
117
111
|
return resp
|
|
118
112
|
except Exception as e:
|
|
119
113
|
if self.retry_times == 0:
|
|
@@ -122,15 +116,19 @@ class AbstractJmClient(
|
|
|
122
116
|
self.before_retry(e, kwargs, retry_count, url)
|
|
123
117
|
|
|
124
118
|
if retry_count < self.retry_times:
|
|
125
|
-
return self.request_with_retry(request, url_backup, domain_index, retry_count + 1,
|
|
119
|
+
return self.request_with_retry(request, url_backup, domain_index, retry_count + 1, is_image, **kwargs)
|
|
126
120
|
else:
|
|
127
|
-
return self.request_with_retry(request, url_backup, domain_index + 1, 0,
|
|
121
|
+
return self.request_with_retry(request, url_backup, domain_index + 1, 0, is_image, **kwargs)
|
|
128
122
|
|
|
129
123
|
# noinspection PyMethodMayBeStatic
|
|
130
|
-
def raise_if_resp_should_retry(self, resp):
|
|
124
|
+
def raise_if_resp_should_retry(self, resp, is_image):
|
|
131
125
|
"""
|
|
132
126
|
依然是回调,在最后返回之前,还可以判断resp是否重试
|
|
133
127
|
"""
|
|
128
|
+
if is_image is True:
|
|
129
|
+
resp = JmImageResp(resp)
|
|
130
|
+
resp.require_success()
|
|
131
|
+
|
|
134
132
|
return resp
|
|
135
133
|
|
|
136
134
|
def update_request_with_specify_domain(self, kwargs: dict, domain: Optional[str], is_image: bool = False):
|
|
@@ -208,7 +206,7 @@ class AbstractJmClient(
|
|
|
208
206
|
self.domain_list = domain_list
|
|
209
207
|
|
|
210
208
|
# noinspection PyUnusedLocal
|
|
211
|
-
def fallback(self, request, url, domain_index, retry_count, **kwargs):
|
|
209
|
+
def fallback(self, request, url, domain_index, retry_count, is_image, **kwargs):
|
|
212
210
|
msg = f"请求重试全部失败: [{url}], {self.domain_list}"
|
|
213
211
|
jm_log('req.fallback', msg)
|
|
214
212
|
ExceptionTool.raises(msg, {}, RequestRetryAllFailException)
|
|
@@ -897,7 +895,7 @@ class JmApiClient(AbstractJmClient):
|
|
|
897
895
|
检查返回数据中的status字段是否为ok
|
|
898
896
|
"""
|
|
899
897
|
data = resp.model_data
|
|
900
|
-
if data.status
|
|
898
|
+
if data.status != 'ok':
|
|
901
899
|
ExceptionTool.raises_resp(data.msg, resp)
|
|
902
900
|
|
|
903
901
|
def req_api(self, url, get=True, require_success=True, **kwargs) -> JmApiResp:
|
|
@@ -965,17 +963,16 @@ class JmApiClient(AbstractJmClient):
|
|
|
965
963
|
# 2. 是否是特殊的内容
|
|
966
964
|
# 暂无
|
|
967
965
|
|
|
968
|
-
def raise_if_resp_should_retry(self, resp):
|
|
966
|
+
def raise_if_resp_should_retry(self, resp, is_image):
|
|
969
967
|
"""
|
|
970
968
|
该方法会判断resp返回值是否是json格式,
|
|
971
969
|
如果不是,大概率是禁漫内部异常,需要进行重试
|
|
972
970
|
|
|
973
971
|
由于完整的json格式校验会有性能开销,所以只做简单的检查,
|
|
974
972
|
只校验第一个有效字符是不是 '{',如果不是,就认为异常数据,需要重试
|
|
975
|
-
|
|
976
|
-
:param resp: 响应对象
|
|
977
|
-
:return: resp
|
|
978
973
|
"""
|
|
974
|
+
resp = super().raise_if_resp_should_retry(resp, is_image)
|
|
975
|
+
|
|
979
976
|
if isinstance(resp, JmResp):
|
|
980
977
|
# 不对包装过的resp对象做校验,包装者自行校验
|
|
981
978
|
# 例如图片请求
|
|
@@ -998,7 +995,7 @@ class JmApiClient(AbstractJmClient):
|
|
|
998
995
|
# 找到第一个有效字符
|
|
999
996
|
ExceptionTool.require_true(
|
|
1000
997
|
char == '{',
|
|
1001
|
-
f'请求不是json格式,强制重试!响应文本: [{
|
|
998
|
+
f'请求不是json格式,强制重试!响应文本: [{JmcomicText.limit_text(text, 200)}]'
|
|
1002
999
|
)
|
|
1003
1000
|
return resp
|
|
1004
1001
|
|
|
@@ -1015,6 +1012,23 @@ class JmApiClient(AbstractJmClient):
|
|
|
1015
1012
|
|
|
1016
1013
|
client_update_domain_lock = Lock()
|
|
1017
1014
|
|
|
1015
|
+
def req_api_domain_server(self, url):
|
|
1016
|
+
resp = self.postman.get(url)
|
|
1017
|
+
text: str = resp.text
|
|
1018
|
+
# 去掉开头非ascii字符
|
|
1019
|
+
while text and not text[0].isascii():
|
|
1020
|
+
text = text[1:]
|
|
1021
|
+
res_json = JmCryptoTool.decode_resp_data(text, '', JmMagicConstants.API_DOMAIN_SERVER_SECRET)
|
|
1022
|
+
res_data = json_loads(res_json)
|
|
1023
|
+
|
|
1024
|
+
# 检查返回值
|
|
1025
|
+
if not res_data.get('Server', None):
|
|
1026
|
+
jm_log('api.update_domain.empty',
|
|
1027
|
+
f'获取禁漫最新API域名失败, 返回值: {res_json}')
|
|
1028
|
+
return None
|
|
1029
|
+
else:
|
|
1030
|
+
return res_data['Server']
|
|
1031
|
+
|
|
1018
1032
|
def update_api_domain(self):
|
|
1019
1033
|
if True is JmModuleConfig.FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN_DONE:
|
|
1020
1034
|
return
|
|
@@ -1022,33 +1036,29 @@ class JmApiClient(AbstractJmClient):
|
|
|
1022
1036
|
with self.client_update_domain_lock:
|
|
1023
1037
|
if True is JmModuleConfig.FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN_DONE:
|
|
1024
1038
|
return
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
jm_log('api.update_domain.
|
|
1034
|
-
f'
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
f'可通过代码[JmModuleConfig.FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN=False]关闭自动更新API域名. 异常: {e}'
|
|
1049
|
-
)
|
|
1050
|
-
finally:
|
|
1051
|
-
JmModuleConfig.FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN_DONE = True
|
|
1039
|
+
# 遍历多个域名服务器
|
|
1040
|
+
for url in JmModuleConfig.API_URL_DOMAIN_SERVER_LIST:
|
|
1041
|
+
try:
|
|
1042
|
+
# 获取域名列表
|
|
1043
|
+
new_server_list = self.req_api_domain_server(url)
|
|
1044
|
+
if new_server_list is None:
|
|
1045
|
+
continue
|
|
1046
|
+
old_server_list = JmModuleConfig.DOMAIN_API_LIST
|
|
1047
|
+
jm_log('api.update_domain.success',
|
|
1048
|
+
f'获取到最新的API域名,替换jmcomic内置域名:(new){new_server_list} ---→ (old){old_server_list}'
|
|
1049
|
+
)
|
|
1050
|
+
# 更新域名
|
|
1051
|
+
if sorted(self.domain_list) == sorted(old_server_list):
|
|
1052
|
+
self.domain_list = new_server_list
|
|
1053
|
+
JmModuleConfig.DOMAIN_API_LIST = new_server_list
|
|
1054
|
+
break
|
|
1055
|
+
except Exception as e:
|
|
1056
|
+
jm_log('api.update_domain.error',
|
|
1057
|
+
f'通过[{url}]自动更新API域名失败,仍使用jmcomic内置域名。'
|
|
1058
|
+
f'可通过代码[JmModuleConfig.FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN=False]关闭自动更新API域名. 异常: {e}'
|
|
1059
|
+
)
|
|
1060
|
+
# set done finally
|
|
1061
|
+
JmModuleConfig.FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN_DONE = True
|
|
1052
1062
|
|
|
1053
1063
|
client_init_cookies_lock = Lock()
|
|
1054
1064
|
|
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_config.py
CHANGED
|
@@ -77,7 +77,7 @@ class JmMagicConstants:
|
|
|
77
77
|
APP_TOKEN_SECRET_2 = '18comicAPPContent'
|
|
78
78
|
APP_DATA_SECRET = '185Hcomic3PAPP7R'
|
|
79
79
|
API_DOMAIN_SERVER_SECRET = 'diosfjckwpqpdfjkvnqQjsik'
|
|
80
|
-
APP_VERSION = '
|
|
80
|
+
APP_VERSION = '2.0.6'
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
# 模块级别共用配置
|
|
@@ -135,7 +135,10 @@ class JmModuleConfig:
|
|
|
135
135
|
''')
|
|
136
136
|
|
|
137
137
|
# 获取最新移动端API域名的地址
|
|
138
|
-
|
|
138
|
+
API_URL_DOMAIN_SERVER_LIST = shuffled('''
|
|
139
|
+
https://rup4a04-c01.tos-ap-southeast-1.bytepluses.com/newsvr-2025.txt
|
|
140
|
+
https://rup4a04-c02.tos-cn-hongkong.bytepluses.com/newsvr-2025.txt
|
|
141
|
+
''')
|
|
139
142
|
|
|
140
143
|
APP_HEADERS_TEMPLATE = {
|
|
141
144
|
'Accept-Encoding': 'gzip, deflate',
|
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
|
@@ -1216,3 +1216,93 @@ class ReplacePathStringPlugin(JmOptionPlugin):
|
|
|
1216
1216
|
return original_path
|
|
1217
1217
|
|
|
1218
1218
|
self.option.decide_image_save_dir = new_decide_dir
|
|
1219
|
+
|
|
1220
|
+
|
|
1221
|
+
class AdvancedRetryPlugin(JmOptionPlugin):
|
|
1222
|
+
plugin_key = 'advanced-retry'
|
|
1223
|
+
|
|
1224
|
+
def __init__(self, option: JmOption):
|
|
1225
|
+
super().__init__(option)
|
|
1226
|
+
self.retry_config = None
|
|
1227
|
+
|
|
1228
|
+
def invoke(self,
|
|
1229
|
+
retry_config,
|
|
1230
|
+
**kwargs):
|
|
1231
|
+
self.require_param(isinstance(retry_config, dict), '必须配置retry_config为dict')
|
|
1232
|
+
self.retry_config = retry_config
|
|
1233
|
+
|
|
1234
|
+
new_jm_client: Callable = self.option.new_jm_client
|
|
1235
|
+
|
|
1236
|
+
def hook_new_jm_client(*args, **kwargs):
|
|
1237
|
+
client: JmcomicClient = new_jm_client(*args, **kwargs)
|
|
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()
|
|
1242
|
+
return client
|
|
1243
|
+
|
|
1244
|
+
self.option.new_jm_client = hook_new_jm_client
|
|
1245
|
+
|
|
1246
|
+
def request_with_retry(self,
|
|
1247
|
+
client: AbstractJmClient,
|
|
1248
|
+
request: Callable,
|
|
1249
|
+
url: str,
|
|
1250
|
+
is_image: bool,
|
|
1251
|
+
**kwargs,
|
|
1252
|
+
):
|
|
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.5.dist-info/RECORD
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
jmcomic/__init__.py,sha256=dK6DD9-2IN7Bo5y5pYeX1-LWii9kAjf8spTfaHfEfmw,902
|
|
2
|
-
jmcomic/api.py,sha256=ZduhXDmh4lg1dkXHs7UTAaPpYNO7kcdPCDH5JJT9KSI,4253
|
|
3
|
-
jmcomic/cl.py,sha256=PBSh0JndNFZw3B7WJPj5Y8SeFdKzHE00jIwYo9An-K0,3475
|
|
4
|
-
jmcomic/jm_client_impl.py,sha256=huPWXkEizu8x7J4rAk_iQROnXphPoyadXyvN8n1ENqI,41624
|
|
5
|
-
jmcomic/jm_client_interface.py,sha256=Bld80cYwBwIqcMSJK09g5q8U_UX3IWnQJxuRJQwmIOk,19082
|
|
6
|
-
jmcomic/jm_config.py,sha256=8oWw7BiQh9Jimhn5Des8Dk0YL4gEnjp31yKBhwiS8sQ,17113
|
|
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=x42eqhRcnHElTif8ORXw2jZh33Xnq55-1dpC6tmr_lQ,40894
|
|
12
|
-
jmcomic/jm_toolkit.py,sha256=SIYCRZ6P4SPbAQA03U5z2DNiDldY7hWsAvKTdEzyK9U,29683
|
|
13
|
-
jmcomic-2.6.5.dist-info/licenses/LICENSE,sha256=kz4coTxZxuGxisK3W00tjK57Zh3RcMGq-EnbXrK7-xA,1064
|
|
14
|
-
jmcomic-2.6.5.dist-info/METADATA,sha256=wz0cF1HnDaeaj6YRev35IYubXqSrIXs7WCJf3ZgiICQ,9572
|
|
15
|
-
jmcomic-2.6.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
-
jmcomic-2.6.5.dist-info/entry_points.txt,sha256=tRbQltaGSBjejI0c9jYt-4SXQMd5nSDHcMvHmuTy4ow,44
|
|
17
|
-
jmcomic-2.6.5.dist-info/top_level.txt,sha256=puvVMFYJqIbd6NOTMEvOyugMTT8woBfSQyxEBan3zY4,8
|
|
18
|
-
jmcomic-2.6.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|