Skip to content

Commit 6ab7456

Browse files
authored
v2.4.4: 优化GitHub Actions、文档、注释、代码结构 (#168)
1 parent f35cb12 commit 6ab7456

16 files changed

+157
-95
lines changed

.github/workflows/download_dispatch.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ on:
1515

1616
CLIENT_IMPL:
1717
type: string
18-
description: 客户端类型(client.impl),[api]=移动端,[html]=网页端。
19-
default: 'api'
18+
description: 客户端类型(client.impl),[api]=移动端,[html]=网页端。下载失败时,你可以尝试填入此项重试。
19+
default: ''
2020
required: false
2121

2222
IMAGE_SUFFIX:

assets/docs/sources/images/6.png

57.9 KB
Loading

assets/docs/sources/tutorial/1_github_actions.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@
2222

2323
## 2. 填写你需要下载的本子id
2424

25+
在开始下面的步骤之前,你需要先启用你的repo的Actions,开启方式如下:
26+
![6](../images/6.png)
27+
28+
2529
### 2.1. 方式一(最新、简单、推荐)
2630

2731
访问下面这个网址:
2832

29-
`https://github.com/你的用户名/JMComic-Crawler-Python/master/workflows/download_dispatch.yml`
33+
`https://github.com/你的用户名/JMComic-Crawler-Python/actions/workflows/download_dispatch.yml`
3034

3135
按下图步骤进行操作:
3236

assets/docs/sources/tutorial/3_demo.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,37 @@ for aid, atitle, tag_list in page.iter_id_title_tag(): # 使用page的iter_id_t
8181
aid_list.append(aid)
8282

8383
download_album(aid_list, option)
84-
```
84+
```
85+
86+
## 手动创建Client
87+
88+
```python
89+
# 默认的使用方式是先创建option,option封装了所有配置,然后由option.new_jm_client() 创建客户端client,使用client可以访问禁漫接口
90+
91+
# 下面演示直接构造client的方式
92+
from jmcomic import *
93+
94+
"""
95+
创建JM客户端
96+
97+
:param postman: 负责实现HTTP请求的对象,持有cookies、headers、proxies等信息
98+
:param domain_list: 禁漫域名
99+
:param retry_times: 重试次数
100+
"""
101+
102+
# 网页端
103+
cl = JmHtmlClient(
104+
postman=JmModuleConfig.new_postman(),
105+
domain_list=['18comic.vip'],
106+
retry_times=1
107+
)
108+
109+
# API端(APP)
110+
cl = JmApiClient(
111+
postman=JmModuleConfig.new_postman(),
112+
domain_list=JmModuleConfig.DOMAIN_API_LIST,
113+
retry_times=1
114+
)
115+
116+
117+
```

assets/option/option_workflow_download.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ dir_rule:
44
rule: Bd_Aauthor_Atitle_Pindex
55

66
client:
7+
impl: api # 使用api可免登录下载本子
78
domain:
89
html: [ jmcomic1.me, jmcomic.me ]
910

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
package_dir={"": "src"},
2828
python_requires=">=3.7",
2929
install_requires=[
30-
'commonX>=0.6.3',
30+
'commonX>=0.6.4',
3131
'curl_cffi',
3232
'PyYAML',
3333
'Pillow',

src/jmcomic/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# 被依赖方 <--- 使用方
33
# config <--- entity <--- toolkit <--- client <--- option <--- downloader
44

5-
__version__ = '2.4.3'
5+
__version__ = '2.4.4'
66

77
from .api import *
88
from .jm_plugin import *

src/jmcomic/jm_client_impl.py

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,19 @@ class AbstractJmClient(
1111

1212
def __init__(self,
1313
postman: Postman,
14-
retry_times: int,
15-
domain=None,
16-
fallback_domain_list=None,
14+
domain_list: List[str],
15+
retry_times=0,
1716
):
17+
"""
18+
创建JM客户端
19+
20+
:param postman: 负责实现HTTP请求的对象,持有cookies、headers、proxies等信息
21+
:param domain_list: 禁漫域名
22+
:param retry_times: 重试次数
23+
"""
1824
super().__init__(postman)
1925
self.retry_times = retry_times
20-
21-
if fallback_domain_list is None:
22-
fallback_domain_list = []
23-
24-
if domain is not None:
25-
fallback_domain_list.insert(0, domain)
26-
27-
self.domain_list = fallback_domain_list
26+
self.domain_list = domain_list
2827
self.CLIENT_CACHE = None
2928
self.enable_cache()
3029
self.after_init()
@@ -98,6 +97,9 @@ def request_with_retry(self,
9897
resp = request(url, **kwargs)
9998
return judge(resp)
10099
except Exception as e:
100+
if self.retry_times == 0:
101+
raise e
102+
101103
self.before_retry(e, kwargs, retry_count, url)
102104

103105
if retry_count < self.retry_times:
@@ -190,7 +192,7 @@ def append_params_to_url(self, url, params):
190192

191193
# noinspection PyMethodMayBeStatic
192194
def decode(self, url: str):
193-
if not JmModuleConfig.decode_url_when_logging or '/search/' not in url:
195+
if not JmModuleConfig.flag_decode_url_when_logging or '/search/' not in url:
194196
return url
195197

196198
from urllib.parse import unquote
@@ -371,7 +373,7 @@ def album_comment(self,
371373
status='true',
372374
comment_id=None,
373375
**kwargs,
374-
) -> JmAcResp:
376+
) -> JmAlbumCommentResp:
375377
data = {
376378
'video_id': video_id,
377379
'comment': comment,
@@ -396,7 +398,7 @@ def album_comment(self,
396398
data=data,
397399
)
398400

399-
ret = JmAcResp(resp)
401+
ret = JmAlbumCommentResp(resp)
400402
jm_log('album.comment', f'{video_id}: [{comment}] ← ({ret.model().cid})')
401403

402404
return ret
@@ -754,15 +756,15 @@ def decide_headers_and_ts(self, kwargs, url):
754756
ts = time_stamp()
755757
token, tokenparam = JmCryptoTool.token_and_tokenparam(ts, secret=JmMagicConstants.APP_TOKEN_SECRET_2)
756758

757-
elif JmModuleConfig.use_fix_timestamp:
759+
elif JmModuleConfig.flag_use_fix_timestamp:
758760
ts, token, tokenparam = JmModuleConfig.get_fix_ts_token_tokenparam()
759761

760762
else:
761763
ts = time_stamp()
762764
token, tokenparam = JmCryptoTool.token_and_tokenparam(ts)
763765

764-
# 计算token,tokenparam
765-
headers = kwargs.get('headers', JmMagicConstants.APP_HEADERS_TEMPLATE.copy())
766+
# 设置headers
767+
headers = kwargs.get('headers', None) or JmMagicConstants.APP_HEADERS_TEMPLATE.copy()
766768
headers.update({
767769
'token': token,
768770
'tokenparam': tokenparam,
@@ -786,7 +788,7 @@ def require_resp_success(cls, resp: JmApiResp, orig_req_url: str):
786788

787789
def after_init(self):
788790
# 保证拥有cookies,因为移动端要求必须携带cookies,否则会直接跳转同一本子【禁漫娘】
789-
if JmModuleConfig.api_client_require_cookies:
791+
if JmModuleConfig.flag_api_client_require_cookies:
790792
self.ensure_have_cookies()
791793

792794
from threading import Lock
@@ -800,7 +802,13 @@ def ensure_have_cookies(self):
800802
if self.get_meta_data('cookies'):
801803
return
802804

803-
self['cookies'] = JmModuleConfig.get_cookies(self)
805+
self['cookies'] = self.get_cookies()
806+
807+
@field_cache("APP_COOKIES", obj=JmModuleConfig)
808+
def get_cookies(self):
809+
resp = self.setting()
810+
cookies = dict(resp.resp.cookies)
811+
return cookies
804812

805813

806814
class FutureClientProxy(JmcomicClient):
@@ -823,19 +831,21 @@ class FutureClientProxy(JmcomicClient):
823831
'set_cache_dict', 'get_cache_dict', 'set_domain_list', ]
824832

825833
class FutureWrapper:
826-
def __init__(self, future):
834+
def __init__(self, future, after_done_callback):
827835
from concurrent.futures import Future
828836
future: Future
829837
self.future = future
830838
self.done = False
831839
self._result = None
840+
self.after_done_callback = after_done_callback
832841

833842
def result(self):
834843
if not self.done:
835844
result = self.future.result()
836845
self._result = result
837846
self.done = True
838847
self.future = None # help gc
848+
self.after_done_callback()
839849

840850
return self._result
841851

@@ -871,7 +881,9 @@ def get_future(self, cache_key, task):
871881
if cache_key in self.future_dict:
872882
return self.future_dict[cache_key]
873883

874-
future = self.FutureWrapper(self.executors.submit(task))
884+
future = self.FutureWrapper(self.executors.submit(task),
885+
after_done_callback=lambda: self.future_dict.pop(cache_key, None)
886+
)
875887
self.future_dict[cache_key] = future
876888
return future
877889

src/jmcomic/jm_client_interface.py

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,46 @@
99
DictModel = AdvancedEasyAccessDict
1010

1111

12-
class JmResp(CommonResp):
12+
class JmResp:
13+
14+
def __init__(self, resp):
15+
ExceptionTool.require_true(not isinstance(resp, JmResp), f'重复包装: {resp}')
16+
self.resp = resp
1317

1418
@property
1519
def is_success(self) -> bool:
1620
return self.http_code == 200 and len(self.content) != 0
1721

18-
def json(self, **kwargs) -> Dict:
19-
raise NotImplementedError
22+
@property
23+
def is_not_success(self) -> bool:
24+
return not self.is_success
2025

21-
def model(self) -> DictModel:
22-
return DictModel(self.json())
26+
@property
27+
def content(self):
28+
return self.resp.content
29+
30+
@property
31+
def http_code(self):
32+
return self.resp.status_code
33+
34+
@property
35+
def text(self) -> str:
36+
return self.resp.text
37+
38+
@property
39+
def url(self) -> str:
40+
return self.resp.url
2341

2442
def require_success(self):
2543
if self.is_not_success:
26-
ExceptionTool.raises_resp(self.text, self.resp)
44+
ExceptionTool.raises_resp(self.text, self)
2745

2846

2947
class JmImageResp(JmResp):
3048

31-
def json(self, **kwargs) -> Dict:
32-
raise NotImplementedError
33-
3449
def require_success(self):
35-
ExceptionTool.require_true(self.is_success, self.get_error_msg())
50+
if self.is_not_success:
51+
ExceptionTool.raises_resp(self.get_error_msg(), self)
3652

3753
def get_error_msg(self):
3854
msg = f'禁漫图片获取失败: [{self.url}]'
@@ -66,21 +82,27 @@ def transfer_to(self,
6682
)
6783

6884

69-
class JmApiResp(JmResp):
85+
class JmJsonResp(JmResp):
7086

71-
def __init__(self, resp, ts: str):
72-
ExceptionTool.require_true(not isinstance(resp, JmApiResp), f'重复包装: {resp}')
87+
def json(self) -> Dict:
88+
return self.resp.json()
89+
90+
def model(self) -> DictModel:
91+
return DictModel(self.json())
92+
93+
94+
class JmApiResp(JmJsonResp):
7395

96+
def __init__(self, resp, ts: str):
7497
super().__init__(resp)
7598
self.ts = ts
76-
self.cache_decode_data = None
7799

78100
@property
79101
def is_success(self) -> bool:
80102
return super().is_success and self.json()['code'] == 200
81103

82104
@property
83-
@field_cache('__cache_decoded_data__')
105+
@field_cache()
84106
def decoded_data(self) -> str:
85107
return JmCryptoTool.decode_resp_data(self.encoded_data, self.ts)
86108

@@ -94,23 +116,19 @@ def res_data(self) -> Any:
94116
from json import loads
95117
return loads(self.decoded_data)
96118

97-
@field_cache('__cache_json__')
98-
def json(self, **kwargs) -> Dict:
99-
return self.resp.json()
100-
101119
@property
102120
def model_data(self) -> DictModel:
103121
self.require_success()
104122
return DictModel(self.res_data)
105123

106124

107125
# album-comment
108-
class JmAcResp(JmResp):
126+
class JmAlbumCommentResp(JmJsonResp):
109127

110128
def is_success(self) -> bool:
111129
return super().is_success and self.json()['err'] is False
112130

113-
def json(self, **kwargs) -> Dict:
131+
def json(self) -> Dict:
114132
return self.resp.json()
115133

116134

@@ -184,7 +202,7 @@ def album_comment(self,
184202
status='true',
185203
comment_id=None,
186204
**kwargs,
187-
) -> JmAcResp:
205+
) -> JmAlbumCommentResp:
188206
"""
189207
评论漫画/评论回复
190208
:param video_id: album_id/photo_id

0 commit comments

Comments
 (0)