Skip to content

Commit 4646146

Browse files
authored
v2.2.8: 优化统一禁漫网页端和移动端的搜索返回类,增加移动端的测试,跟进文档 (#132)
1 parent e0cc0b0 commit 4646146

14 files changed

+233
-133
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: 跑测试
22

33
on:
44
schedule:
5-
- cron: "0 8 * * *"
5+
- cron: "0 0 * * *"
66
workflow_dispatch:
77
push:
88
branches: [ "dev" ]

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ jmcomic.download_album('422866') # 传入要下载的album的id,即可下载
7070
- 配置点有:`是否使用磁盘缓存` `并发下载图片数` `图片类型转换` `下载路径` `请求元信息(headers,cookies,proxies)`
7171
- **可扩展性强**
7272
- **支持Plugin插件,可以方便地扩展功能,以及使用别人的插件**
73-
- 目前内置支持的插件有:`登录插件` `硬件占用监控插件` `只下载新章插件` `压缩文件插件`
73+
- 目前内置支持的插件有:`登录插件` `硬件占用监控插件` `只下载新章插件` `压缩文件插件`
7474
- 支持自定义本子/章节/图片下载前后的回调函数
75-
- 支持自定义debug日志的开关/格式
76-
- 支持自定义Downloader/Option/Client/实体类
75+
- 支持自定义debug日志
76+
- 支持自定义类:Downloader(负责调度)/Option(负责配置)/Client(负责请求)/实体类 等等
7777
- ......
7878
- 支持**自动重试和域名切换**机制
7979
- **多线程下载**(可细化到一图一线程,效率极高)

requirements-dev.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ commonX
22
curl_cffi
33
PyYAML
44
Pillow
5-
psutil
5+
psutil
6+
pycryptodome

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.2.7'
5+
__version__ = '2.2.8'
66

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

src/jmcomic/jm_client_impl.py

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ def search(self,
225225
album = JmcomicText.analyse_jm_album_html(resp.text)
226226
return JmSearchPage.wrap_single_album(album)
227227
else:
228-
return JmSearchSupport.analyse_jm_search_html(resp.text)
228+
return JmcomicText.analyse_jm_search_html(resp.text)
229229

230230
# -- 帐号管理 --
231231

@@ -405,31 +405,8 @@ def search(self,
405405
main_tag: int,
406406
order_by: str,
407407
time: str,
408-
) -> JmApiResp:
409-
"""
410-
model_data: {
411-
"search_query": "MANA",
412-
"total": "177",
413-
"content": [
414-
{
415-
"id": "441923",
416-
"author": "MANA",
417-
"description": "",
418-
"name": "[MANA] 神里绫华5",
419-
"image": "",
420-
"category": {
421-
"id": "1",
422-
"title": "同人"
423-
},
424-
"category_sub": {
425-
"id": "1",
426-
"title": "同人"
427-
}
428-
}
429-
]
430-
}
431-
"""
432-
return self.get(
408+
) -> JmSearchPage:
409+
resp = self.get(
433410
self.API_SEARCH,
434411
params={
435412
'search_query': search_query,
@@ -440,6 +417,20 @@ def search(self,
440417
}
441418
)
442419

420+
# 直接搜索禁漫车号,发生重定向的响应数据 resp.model_data
421+
# {
422+
# "search_query": "310311",
423+
# "total": 1,
424+
# "redirect_aid": "310311",
425+
# "content": []
426+
# }
427+
data = resp.model_data
428+
if data.get('redirect_aid', None) is not None:
429+
aid = data.redirect_aid
430+
return JmSearchPage.wrap_single_album(self.get_album_detail(aid))
431+
432+
return JmcomicSearchTool.parse_api_resp_to_page(data)
433+
443434
def get(self, url, **kwargs) -> JmApiResp:
444435
# set headers
445436
headers, key_ts = self.headers_key_ts
@@ -464,17 +455,5 @@ def debug_topic_request(self):
464455
return 'api'
465456

466457

467-
class AsyncSaveImageClient(JmImageClient):
468-
469-
def __init__(self, workers=None) -> None:
470-
from concurrent.futures import ThreadPoolExecutor, Future
471-
self.executor = ThreadPoolExecutor(max_workers=workers)
472-
self.future_list: List[Future] = []
473-
474-
def save_image_resp(self, *args, **kwargs):
475-
future = self.executor.submit(lambda: super().save_image_resp(*args, **kwargs))
476-
self.future_list.append(future)
477-
478-
479458
JmModuleConfig.register_client(JmHtmlClient)
480459
JmModuleConfig.register_client(JmApiClient)

src/jmcomic/jm_client_interface.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
77
"""
88

9+
DictModel = AdvancedEasyAccessDict
910

1011
class JmResp(CommonResp):
1112

@@ -19,6 +20,9 @@ def json(self, **kwargs) -> Dict:
1920
def model(self) -> DictModel:
2021
return DictModel(self.json())
2122

23+
def require_success(self):
24+
if self.is_not_success:
25+
raise JmModuleConfig.exception(self.resp.text)
2226

2327
class JmImageResp(JmResp):
2428

@@ -312,7 +316,7 @@ def search_site(self,
312316
page: int = 1,
313317
order_by: str = ORDER_BY_LATEST,
314318
time: str = TIME_ALL,
315-
) -> JmSearchPage:
319+
):
316320
"""
317321
对应禁漫的站内搜索
318322
"""
@@ -323,7 +327,7 @@ def search_work(self,
323327
page: int = 1,
324328
order_by: str = ORDER_BY_LATEST,
325329
time: str = TIME_ALL,
326-
) -> JmSearchPage:
330+
):
327331
"""
328332
搜索album的作品 work
329333
"""
@@ -334,7 +338,7 @@ def search_author(self,
334338
page: int = 1,
335339
order_by: str = ORDER_BY_LATEST,
336340
time: str = TIME_ALL,
337-
) -> JmSearchPage:
341+
):
338342
"""
339343
搜索album的作者 author
340344
"""
@@ -345,7 +349,7 @@ def search_tag(self,
345349
page: int = 1,
346350
order_by: str = ORDER_BY_LATEST,
347351
time: str = TIME_ALL,
348-
) -> JmSearchPage:
352+
):
349353
"""
350354
搜索album的标签 tag
351355
"""
@@ -356,7 +360,7 @@ def search_actor(self,
356360
page: int = 1,
357361
order_by: str = ORDER_BY_LATEST,
358362
time: str = TIME_ALL,
359-
) -> JmSearchPage:
363+
):
360364
"""
361365
搜索album的登场角色 actor
362366
"""

src/jmcomic/jm_entity.py

Lines changed: 76 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,19 @@
55

66
class JmBaseEntity:
77

8-
@staticmethod
9-
def fix_title(title: str, limit=50):
10-
"""
11-
一些过长的标题可能含有 \n,例如album: 360537
12-
该方法会把 \n 去除
13-
"""
14-
if len(title) > limit and '\n' in title:
15-
title = title.replace('\n', '')
16-
17-
return title.strip()
18-
198
def save_to_file(self, filepath):
209
from common import PackerUtil
2110
PackerUtil.pack(self, filepath)
2211

2312

24-
class DetailEntity(JmBaseEntity):
25-
26-
@property
27-
def id(self) -> str:
28-
raise NotImplementedError
29-
30-
@property
31-
def name(self) -> str:
32-
return getattr(self, 'title')
33-
34-
# help for typing
35-
JMPI = Union['JmPhotoDetail', 'JmImageDetail']
36-
37-
def getindex(self, index: int) -> JMPI:
13+
class IndexedEntity:
14+
def getindex(self, index: int):
3815
raise NotImplementedError
3916

4017
def __len__(self):
4118
raise NotImplementedError
4219

43-
def __iter__(self) -> Generator[JMPI, Any, None]:
44-
for index in range(len(self)):
45-
yield self.getindex(index)
46-
47-
def __str__(self):
48-
return f'{self.__class__.__name__}({self.id}-{self.name})'
49-
50-
def __getitem__(self, item) -> Union[JMPI, List[JMPI]]:
20+
def __getitem__(self, item) -> Any:
5121
if isinstance(item, slice):
5222
start = item.start or 0
5323
stop = item.stop or len(self)
@@ -60,6 +30,24 @@ def __getitem__(self, item) -> Union[JMPI, List[JMPI]]:
6030
else:
6131
raise TypeError(f"Invalid item type for {self.__class__}")
6232

33+
def __iter__(self):
34+
for index in range(len(self)):
35+
yield self.getindex(index)
36+
37+
38+
class DetailEntity(JmBaseEntity, IndexedEntity):
39+
40+
@property
41+
def id(self) -> str:
42+
raise NotImplementedError
43+
44+
@property
45+
def name(self) -> str:
46+
return getattr(self, 'title')
47+
48+
def __str__(self):
49+
return f'{self.__class__.__name__}({self.id}-{self.name})'
50+
6351
@classmethod
6452
def __alias__(cls):
6553
# "JmAlbumDetail" -> "album" (本子)
@@ -163,7 +151,7 @@ def __init__(self,
163151
):
164152
self.photo_id: str = photo_id
165153
self.scramble_id: str = scramble_id
166-
self.title: str = self.fix_title(str(title))
154+
self.title: str = str(title).strip()
167155
self.sort: int = int(sort)
168156
self._keywords: str = keywords
169157
self._series_id: int = int(series_id)
@@ -290,7 +278,7 @@ def __getitem__(self, item) -> Union[JmImageDetail, List[JmImageDetail]]:
290278
def __len__(self):
291279
return len(self.page_arr)
292280

293-
def __iter__(self) -> Generator[JmImageDetail, Any, None]:
281+
def __iter__(self) -> Generator[JmImageDetail, None, None]:
294282
return super().__iter__()
295283

296284

@@ -418,39 +406,71 @@ def __getitem__(self, item) -> Union[JmPhotoDetail, List[JmPhotoDetail]]:
418406
def __len__(self):
419407
return len(self.episode_list)
420408

421-
def __iter__(self) -> Generator[JmPhotoDetail, Any, None]:
409+
def __iter__(self) -> Generator[JmPhotoDetail, None, None]:
422410
return super().__iter__()
423411

424412

425-
class JmSearchPage(JmBaseEntity):
413+
class JmSearchPage(JmBaseEntity, IndexedEntity):
414+
ContentItem = Tuple[str, Dict[str, Any]]
426415

427-
def __init__(self, album_info_list: List[Tuple[str, str, StrNone, StrNone, List[str]]]):
428-
# (album_id, title, category_none, label_sub_none, tag_list)
429-
self.album_info_list = album_info_list
416+
def __init__(self, content: List[ContentItem]):
417+
# [
418+
# album_id, {title, tag_list, ...}
419+
# ]
420+
self.content = content
430421

431-
def __len__(self):
432-
return len(self.album_info_list)
422+
def iter_id(self) -> Generator[str, None, None]:
423+
"""
424+
返回 album_id 的迭代器
425+
"""
426+
for aid, ainfo in self.content:
427+
yield aid
428+
429+
def iter_id_title(self) -> Generator[Tuple[str, str], None, None]:
430+
"""
431+
返回 album_id, album_title 的迭代器
432+
"""
433+
for aid, ainfo in self.content:
434+
yield aid, ainfo['name']
433435

434-
def __getitem__(self, item) -> Tuple[str, str]:
435-
return self.album_info_list[item][0:2]
436+
def iter_id_title_tag(self) -> Generator[Tuple[str, str, List[str]], None, None]:
437+
"""
438+
返回 album_id, album_title, album_tag_list 的迭代器
439+
"""
440+
for aid, ainfo in self.content:
441+
yield aid, ainfo['name'], ainfo['tag_list']
442+
443+
# 下面的方法是对单个album的包装
444+
445+
@property
446+
def is_single_album(self):
447+
return hasattr(self, 'album')
436448

437449
@property
438450
def single_album(self) -> JmAlbumDetail:
439451
return getattr(self, 'album')
440452

441453
@classmethod
442454
def wrap_single_album(cls, album: JmAlbumDetail) -> 'JmSearchPage':
443-
# ('462257', '[無邪気漢化組] [きょくちょ] 楓と鈴 4.5', '短篇', '漢化', [])
444-
# (album_id, title, category_none, label_sub_none, tag_list)
445-
446-
album_info = (
447-
album.album_id,
448-
album.title,
449-
None,
450-
None,
451-
album.tag_list,
452-
)
453-
obj = JmSearchPage([album_info])
454-
455+
obj = JmSearchPage([(
456+
album.album_id, {
457+
'name': album.title,
458+
'tag_list': album.tag_list,
459+
}
460+
)])
455461
setattr(obj, 'album', album)
456462
return obj
463+
464+
# 下面的方法实现方便的元素访问
465+
466+
def __len__(self):
467+
return len(self.content)
468+
469+
def __iter__(self):
470+
return self.iter_id_title()
471+
472+
def __getitem__(self, item) -> Union[ContentItem, List[ContentItem]]:
473+
return super().__getitem__(item)
474+
475+
def getindex(self, index: int):
476+
return self.content[index]

src/jmcomic/jm_option.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,11 @@ def __init__(self,
130130
# 路径规则配置
131131
self.dir_rule = DirRule(**dir_rule)
132132
# 请求配置
133-
self.client = DictModel(client)
133+
self.client = AdvancedEasyAccessDict(client)
134134
# 下载配置
135-
self.download = DictModel(download)
135+
self.download = AdvancedEasyAccessDict(download)
136136
# 插件配置
137-
self.plugin = DictModel(plugin)
137+
self.plugin = AdvancedEasyAccessDict(plugin)
138138
# 其他配置
139139
self.filepath = filepath
140140

0 commit comments

Comments
 (0)