@@ -26,6 +26,7 @@ def __init__(self,
2626 super ().__init__ (postman )
2727 self .retry_times = retry_times
2828 self .domain_list = domain_list
29+ self .domain_retry_strategy = None
2930 self .CLIENT_CACHE = None
3031 self ._username = None # help for favorite_folder method
3132 self .enable_cache ()
@@ -44,23 +45,14 @@ def of_api_url(self, api_path, domain):
4445 return JmcomicText .format_url (api_path , domain )
4546
4647 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 ())
5749
5850 def request_with_retry (self ,
5951 request ,
6052 url ,
6153 domain_index = 0 ,
6254 retry_count = 0 ,
63- callback = None ,
55+ is_image = False ,
6456 ** kwargs ,
6557 ):
6658 """
@@ -74,11 +66,19 @@ def request_with_retry(self,
7466 :param url: 图片url / path (/album/xxx)
7567 :param domain_index: 域名下标
7668 :param retry_count: 重试次数
77- :param callback: 回调,可以接收resp返回新的resp,也可以抛出异常强制重试
69+ :param is_image: 是否是图片请求
7870 :param kwargs: 请求方法的kwargs
7971 """
72+ if self .domain_retry_strategy :
73+ return self .domain_retry_strategy (self ,
74+ request ,
75+ url ,
76+ is_image ,
77+ ** kwargs ,
78+ )
79+
8080 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 )
8282
8383 url_backup = url
8484
@@ -87,12 +87,12 @@ def request_with_retry(self,
8787 domain = self .domain_list [domain_index ]
8888 url = self .of_api_url (url , domain )
8989
90- self .update_request_with_specify_domain (kwargs , domain )
90+ self .update_request_with_specify_domain (kwargs , domain , is_image )
9191
9292 jm_log (self .log_topic (), self .decode (url ))
93- else :
93+ elif is_image :
9494 # 图片url
95- self .update_request_with_specify_domain (kwargs , None , True )
95+ self .update_request_with_specify_domain (kwargs , None , is_image )
9696
9797 if domain_index != 0 or retry_count != 0 :
9898 jm_log (f'req.retry' ,
@@ -106,14 +106,8 @@ def request_with_retry(self,
106106
107107 try :
108108 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 )
117111 return resp
118112 except Exception as e :
119113 if self .retry_times == 0 :
@@ -122,15 +116,19 @@ def request_with_retry(self,
122116 self .before_retry (e , kwargs , retry_count , url )
123117
124118 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 )
126120 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 )
128122
129123 # noinspection PyMethodMayBeStatic
130- def raise_if_resp_should_retry (self , resp ):
124+ def raise_if_resp_should_retry (self , resp , is_image ):
131125 """
132126 依然是回调,在最后返回之前,还可以判断resp是否重试
133127 """
128+ if is_image is True :
129+ resp = JmImageResp (resp )
130+ resp .require_success ()
131+
134132 return resp
135133
136134 def update_request_with_specify_domain (self , kwargs : dict , domain : Optional [str ], is_image : bool = False ):
@@ -208,7 +206,7 @@ def set_domain_list(self, domain_list: List[str]):
208206 self .domain_list = domain_list
209207
210208 # 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 ):
212210 msg = f"请求重试全部失败: [{ url } ], { self .domain_list } "
213211 jm_log ('req.fallback' , msg )
214212 ExceptionTool .raises (msg , {}, RequestRetryAllFailException )
@@ -965,17 +963,16 @@ def require_resp_success(cls, resp: JmApiResp, url: Optional[str] = None):
965963 # 2. 是否是特殊的内容
966964 # 暂无
967965
968- def raise_if_resp_should_retry (self , resp ):
966+ def raise_if_resp_should_retry (self , resp , is_image ):
969967 """
970968 该方法会判断resp返回值是否是json格式,
971969 如果不是,大概率是禁漫内部异常,需要进行重试
972970
973971 由于完整的json格式校验会有性能开销,所以只做简单的检查,
974972 只校验第一个有效字符是不是 '{',如果不是,就认为异常数据,需要重试
975-
976- :param resp: 响应对象
977- :return: resp
978973 """
974+ resp = super ().raise_if_resp_should_retry (resp , is_image )
975+
979976 if isinstance (resp , JmResp ):
980977 # 不对包装过的resp对象做校验,包装者自行校验
981978 # 例如图片请求
@@ -1015,40 +1012,53 @@ def after_init(self):
10151012
10161013 client_update_domain_lock = Lock ()
10171014
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+
10181032 def update_api_domain (self ):
10191033 if True is JmModuleConfig .FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN_DONE :
10201034 return
10211035
10221036 with self .client_update_domain_lock :
10231037 if True is JmModuleConfig .FLAG_API_CLIENT_AUTO_UPDATE_DOMAIN_DONE :
10241038 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
10521062
10531063 client_init_cookies_lock = Lock ()
10541064
0 commit comments