jmcomic 2.6.5__tar.gz → 2.6.6__tar.gz

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.
Files changed (24) hide show
  1. {jmcomic-2.6.5/src/jmcomic.egg-info → jmcomic-2.6.6}/PKG-INFO +1 -1
  2. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic/__init__.py +1 -1
  3. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic/jm_client_impl.py +69 -59
  4. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic/jm_config.py +5 -2
  5. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic/jm_plugin.py +25 -0
  6. {jmcomic-2.6.5 → jmcomic-2.6.6/src/jmcomic.egg-info}/PKG-INFO +1 -1
  7. {jmcomic-2.6.5 → jmcomic-2.6.6}/LICENSE +0 -0
  8. {jmcomic-2.6.5 → jmcomic-2.6.6}/README.md +0 -0
  9. {jmcomic-2.6.5 → jmcomic-2.6.6}/pyproject.toml +0 -0
  10. {jmcomic-2.6.5 → jmcomic-2.6.6}/setup.cfg +0 -0
  11. {jmcomic-2.6.5 → jmcomic-2.6.6}/setup.py +0 -0
  12. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic/api.py +0 -0
  13. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic/cl.py +0 -0
  14. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic/jm_client_interface.py +0 -0
  15. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic/jm_downloader.py +0 -0
  16. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic/jm_entity.py +0 -0
  17. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic/jm_exception.py +0 -0
  18. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic/jm_option.py +0 -0
  19. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic/jm_toolkit.py +0 -0
  20. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic.egg-info/SOURCES.txt +0 -0
  21. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic.egg-info/dependency_links.txt +0 -0
  22. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic.egg-info/entry_points.txt +0 -0
  23. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic.egg-info/requires.txt +0 -0
  24. {jmcomic-2.6.5 → jmcomic-2.6.6}/src/jmcomic.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jmcomic
3
- Version: 2.6.5
3
+ Version: 2.6.6
4
4
  Summary: Python API For JMComic (禁漫天堂)
5
5
  Home-page: https://github.com/hect0x7/JMComic-Crawler-Python
6
6
  Author: hect0x7
@@ -2,7 +2,7 @@
2
2
  # 被依赖方 <--- 使用方
3
3
  # config <--- entity <--- toolkit <--- client <--- option <--- downloader
4
4
 
5
- __version__ = '2.6.5'
5
+ __version__ = '2.6.6'
6
6
 
7
7
  from .api import *
8
8
  from .jm_plugin import *
@@ -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
- callback=None,
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 callback: 回调,可以接收resp返回新的resp,也可以抛出异常强制重试
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
- else:
93
+ elif is_image:
94
94
  # 图片url
95
- self.update_request_with_specify_domain(kwargs, None, True)
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
- # 回调,可以接收resp返回新的resp,也可以抛出异常强制重试
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, callback, **kwargs)
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, callback, **kwargs)
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)
@@ -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
  # 例如图片请求
@@ -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
- try:
1026
- # 获取域名列表
1027
- resp = self.postman.get(JmModuleConfig.API_URL_DOMAIN_SERVER)
1028
- res_json = JmCryptoTool.decode_resp_data(resp.text, '', JmMagicConstants.API_DOMAIN_SERVER_SECRET)
1029
- res_data = json_loads(res_json)
1030
-
1031
- # 检查返回值
1032
- if not res_data.get('Server', None):
1033
- jm_log('api.update_domain.empty',
1034
- f'获取禁漫最新API域名失败, 返回值: {res_json}')
1035
- return
1036
- new_server_list: List[str] = res_data['Server']
1037
- old_server_list = JmModuleConfig.DOMAIN_API_LIST
1038
- jm_log('api.update_domain.success',
1039
- f'获取到最新的API域名,替换jmcomic内置域名:(new){new_server_list} ---→ (old){old_server_list}'
1040
- )
1041
- # 更新域名
1042
- if self.domain_list is old_server_list:
1043
- self.domain_list = new_server_list
1044
- JmModuleConfig.DOMAIN_API_LIST = new_server_list
1045
- except Exception as e:
1046
- jm_log('api.update_domain.error',
1047
- f'自动更新API域名失败,仍使用jmcomic内置域名。'
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
 
@@ -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 = '1.8.0'
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
- API_URL_DOMAIN_SERVER = f'{PROT}jmapp03-1308024008.cos.ap-jakarta.myqcloud.com/server-2024.txt'
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',
@@ -1216,3 +1216,28 @@ 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 invoke(self,
1225
+ retry_config,
1226
+ **kwargs):
1227
+ new_jm_client: Callable = self.option.new_jm_client
1228
+
1229
+ def hook_new_jm_client(*args, **kwargs):
1230
+ client: AbstractJmClient = new_jm_client(*args, **kwargs)
1231
+ client.domain_retry_strategy = self.request_with_retry
1232
+ return client
1233
+
1234
+ self.option.new_jm_client = hook_new_jm_client
1235
+
1236
+ def request_with_retry(self,
1237
+ client,
1238
+ request,
1239
+ url,
1240
+ is_image,
1241
+ **kwargs,
1242
+ ):
1243
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jmcomic
3
- Version: 2.6.5
3
+ Version: 2.6.6
4
4
  Summary: Python API For JMComic (禁漫天堂)
5
5
  Home-page: https://github.com/hect0x7/JMComic-Crawler-Python
6
6
  Author: hect0x7
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes