Skip to content

Commit dfd3fd6

Browse files
committed
v2.4.9: 实现分类、热门排行榜功能 (#180)
1 parent f6692eb commit dfd3fd6

File tree

9 files changed

+189
-20
lines changed

9 files changed

+189
-20
lines changed

.github/workflows/close_specific_pr.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Close specific PR
1+
name: Close Specific PR
22

33
on:
44
pull_request:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
本项目的核心功能是下载本子,基于此,设计了一套方便使用、便于扩展,能满足一些特殊下载需求的框架。
1212

13-
除了下载功能以外,也实现了其他的一些禁漫接口,例如登录、搜索、收藏夹等等,按需实现。
13+
除了下载功能以外,也实现了其他的一些禁漫接口,例如登录、搜索、收藏夹、分类、排行榜等等,按需实现。
1414

1515
目前核心功能实现较为稳定,项目也处于维护阶段(因为禁漫接口经常变动,需要经常维护)。
1616

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

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

src/jmcomic/jm_client_impl.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,28 @@ def search(self,
309309
album = JmcomicText.analyse_jm_album_html(resp.text)
310310
return JmSearchPage.wrap_single_album(album)
311311
else:
312-
return JmcomicText.analyse_jm_search_html(resp.text)
312+
return JmPageTool.parse_html_to_search_page(resp.text)
313+
314+
def categories_filter(self,
315+
page: int,
316+
time: str,
317+
category: str,
318+
order_by: str,
319+
) -> JmCategoryPage:
320+
params = {
321+
'page': page,
322+
'o': order_by,
323+
't': time,
324+
}
325+
326+
url = f'/albums/' + (category if category != JmMagicConstants.CATEGORY_ALL else '')
327+
328+
resp = self.get_jm_html(
329+
self.append_params_to_url(url, params),
330+
allow_redirects=True,
331+
)
332+
333+
return JmPageTool.parse_html_to_category_page(resp.text)
313334

314335
# -- 帐号管理 --
315336

@@ -525,6 +546,7 @@ class JmApiClient(AbstractJmClient):
525546
func_to_cache = ['search', 'fetch_detail_entity']
526547

527548
API_SEARCH = '/search'
549+
API_CATEGORIES_FILTER = '/categories/filter'
528550
API_ALBUM = '/album'
529551
API_CHAPTER = '/chapter'
530552
API_SCRAMBLE = '/chapter_view_template'
@@ -561,6 +583,26 @@ def search(self,
561583

562584
return JmPageTool.parse_api_to_search_page(data)
563585

586+
def categories_filter(self,
587+
page: int,
588+
time: str,
589+
category: str,
590+
order_by: str,
591+
):
592+
# o: mv, mv_m, mv_w, mv_t
593+
o = f'{order_by}_{time}' if time != JmMagicConstants.TIME_ALL else order_by
594+
595+
params = {
596+
'page': page,
597+
'order': '', # 该参数为空
598+
'c': category,
599+
'o': o,
600+
}
601+
602+
resp = self.req_api(self.append_params_to_url(self.API_CATEGORIES_FILTER, params))
603+
604+
return JmPageTool.parse_api_to_search_page(resp.model_data)
605+
564606
def get_album_detail(self, album_id) -> JmAlbumDetail:
565607
return self.fetch_detail_entity(album_id,
566608
JmModuleConfig.album_class(),

src/jmcomic/jm_client_interface.py

Lines changed: 93 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def transfer_to(self,
8484

8585
class JmJsonResp(JmResp):
8686

87+
@field_cache()
8788
def json(self) -> Dict:
8889
return self.resp.json()
8990

@@ -128,9 +129,6 @@ class JmAlbumCommentResp(JmJsonResp):
128129
def is_success(self) -> bool:
129130
return super().is_success and self.json()['err'] is False
130131

131-
def json(self) -> Dict:
132-
return self.resp.json()
133-
134132

135133
"""
136134
@@ -151,15 +149,6 @@ def get_photo_detail(self,
151149
) -> JmPhotoDetail:
152150
raise NotImplementedError
153151

154-
def of_api_url(self, api_path, domain):
155-
raise NotImplementedError
156-
157-
def set_cache_dict(self, cache_dict: Optional[Dict]):
158-
raise NotImplementedError
159-
160-
def get_cache_dict(self) -> Optional[Dict]:
161-
raise NotImplementedError
162-
163152
def check_photo(self, photo: JmPhotoDetail):
164153
"""
165154
photo来源有两种:
@@ -381,12 +370,78 @@ def search_actor(self,
381370
return self.search(search_query, page, 4, order_by, time)
382371

383372

373+
class JmCategoryClient:
374+
"""
375+
该接口可以看作是对全体禁漫本子的排行,热门排行的功能也派生于此
376+
377+
月排行 = 分类【时间=月,排序=观看】
378+
周排行 = 分类【时间=周,排序=观看】
379+
日排行 = 分类【时间=周,排序=观看】
380+
"""
381+
382+
def categories_filter(self,
383+
page: int,
384+
time: str,
385+
category: str,
386+
order_by: str,
387+
) -> JmCategoryPage:
388+
"""
389+
分类
390+
391+
:param page: 页码
392+
:param time: 时间范围,默认是全部时间
393+
:param category: 类别,默认是最新,即显示最新的禁漫本子
394+
:param order_by: 排序方式,默认是观看数
395+
"""
396+
raise NotImplementedError
397+
398+
def month_ranking(self,
399+
page: int,
400+
category: str = JmMagicConstants.CATEGORY_ALL,
401+
):
402+
"""
403+
月排行 = 分类【时间=月,排序=观看】
404+
"""
405+
return self.categories_filter(page,
406+
JmMagicConstants.TIME_MONTH,
407+
category,
408+
JmMagicConstants.ORDER_BY_VIEW,
409+
)
410+
411+
def week_ranking(self,
412+
page: int,
413+
category: str = JmMagicConstants.CATEGORY_ALL,
414+
):
415+
"""
416+
周排行 = 分类【时间=周,排序=观看】
417+
"""
418+
return self.categories_filter(page,
419+
JmMagicConstants.TIME_WEEK,
420+
category,
421+
JmMagicConstants.ORDER_BY_VIEW,
422+
)
423+
424+
def day_ranking(self,
425+
page: int,
426+
category: str = JmMagicConstants.CATEGORY_ALL,
427+
):
428+
"""
429+
日排行 = 分类【时间=日,排序=观看】
430+
"""
431+
return self.categories_filter(page,
432+
JmMagicConstants.TIME_TODAY,
433+
category,
434+
JmMagicConstants.ORDER_BY_VIEW,
435+
)
436+
437+
384438
# noinspection PyAbstractClass
385439
class JmcomicClient(
386440
JmImageClient,
387441
JmDetailClient,
388442
JmUserClient,
389443
JmSearchAlbumClient,
444+
JmCategoryClient,
390445
Postman,
391446
):
392447
client_key: None
@@ -403,6 +458,15 @@ def set_domain_list(self, domain_list: List[str]):
403458
"""
404459
raise NotImplementedError
405460

461+
def set_cache_dict(self, cache_dict: Optional[Dict]):
462+
raise NotImplementedError
463+
464+
def get_cache_dict(self) -> Optional[Dict]:
465+
raise NotImplementedError
466+
467+
def of_api_url(self, api_path, domain):
468+
raise NotImplementedError
469+
406470
def get_html_domain(self, postman=None):
407471
return JmModuleConfig.get_html_domain(postman or self.get_root_postman())
408472

@@ -487,3 +551,20 @@ def search_gen(self,
487551
}
488552

489553
yield from self.do_page_iter(params, page, self.search)
554+
555+
def categories_filter_gen(self,
556+
page: int = 1,
557+
time: str = JmMagicConstants.TIME_ALL,
558+
category: str = JmMagicConstants.CATEGORY_ALL,
559+
order_by: str = JmMagicConstants.ORDER_BY_LATEST,
560+
) -> Generator[JmCategoryPage, Dict, None]:
561+
"""
562+
见 search_gen
563+
"""
564+
params = {
565+
'time': time,
566+
'category': category,
567+
'order_by': order_by,
568+
}
569+
570+
yield from self.do_page_iter(params, page, self.categories_filter)

src/jmcomic/jm_config.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,28 @@ class JmMagicConstants:
2222
ORDER_BY_PICTURE = 'mp'
2323
ORDER_BY_LIKE = 'tf'
2424

25+
ORDER_MONTH_RANKING = 'mv_m'
26+
ORDER_WEEK_RANKING = 'mv_w'
27+
ORDER_DAY_RANKING = 'mv_t'
28+
2529
# 搜索参数-时间段
2630
TIME_TODAY = 't'
2731
TIME_WEEK = 'w'
2832
TIME_MONTH = 'm'
2933
TIME_ALL = 'a'
3034

35+
# 全部, 同人, 单本, 短篇, 其他, 韩漫, 美漫, cosplay, 3D
36+
# category = ["0", "doujin", "single", "short", "another", "hanman", "meiman", "doujin_cosplay", "3D"]
37+
CATEGORY_ALL = '0'
38+
CATEGORY_DOUJIN = 'doujin'
39+
CATEGORY_SINGLE = 'single'
40+
CATEGORY_SHORT = 'short'
41+
CATEGORY_ANOTHER = 'another'
42+
CATEGORY_HANMAN = 'hanman'
43+
CATEGORY_MEIMAN = 'meiman'
44+
CATEGORY_DOUJIN_COSPLAY = 'doujin_cosplay'
45+
CATEGORY_3D = '3D'
46+
3147
# 分页大小
3248
PAGE_SIZE_SEARCH = 80
3349
PAGE_SIZE_FAVORITE = 20

src/jmcomic/jm_entity.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,9 @@ def wrap_single_album(cls, album: JmAlbumDetail) -> 'JmSearchPage':
617617
return page
618618

619619

620+
JmCategoryPage = JmSearchPage
621+
622+
620623
class JmFavoritePage(JmPageContent):
621624

622625
def __init__(self, content, folder_list, total):

src/jmcomic/jm_option.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ def decide_album_dir(self, album: JmAlbumDetail) -> str:
220220
break
221221

222222
from os.path import join
223+
# noinspection PyTypeChecker
223224
return join(*dir_layer)
224225

225226
# noinspection PyMethodMayBeStatic
@@ -293,6 +294,7 @@ def construct(cls, origdic: Dict, cover_default=True) -> 'JmOption':
293294

294295
# version
295296
version = dic.pop('version', None)
297+
# noinspection PyTypeChecker
296298
if version is not None and float(version) >= float(JmModuleConfig.JM_OPTION_VER):
297299
# 版本号更高,跳过兼容代码
298300
return cls(**dic)

src/jmcomic/jm_toolkit.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,6 @@ def analyse_jm_album_html(cls, html: str) -> JmAlbumDetail:
116116
JmModuleConfig.album_class()
117117
)
118118

119-
@classmethod
120-
def analyse_jm_search_html(cls, html: str) -> JmSearchPage:
121-
return JmPageTool.parse_html_to_search_page(html)
122-
123119
@classmethod
124120
def reflect_new_instance(cls, html: str, cls_field_prefix: str, clazz: type):
125121

@@ -347,6 +343,15 @@ class JmPageTool:
347343
r'(<a[\s\S]*?) </div>'
348344
)
349345

346+
# 用来提取分类页面的的album的信息
347+
pattern_html_category_album_info_list = compile(
348+
r'<a href="/album/(\d+)/[^>]*>[\s\S]*?title="(.*?)"[^>]*>'
349+
r'\n</a>\n'
350+
r'<div class="label-loveicon">'
351+
r'([\s\S]*?)'
352+
r'<div class="clearfix">'
353+
)
354+
350355
# 用来查找tag列表
351356
pattern_html_search_tag_list = compile(r'<a[^>]*?>(.*?)</a>')
352357

@@ -405,6 +410,26 @@ def parse_html_to_search_page(cls, html: str) -> JmSearchPage:
405410

406411
return JmSearchPage(content, total)
407412

413+
@classmethod
414+
def parse_html_to_category_page(cls, html: str) -> JmSearchPage:
415+
# 3. 提取结果
416+
content = []
417+
total = int(PatternTool.match_or_default(html, *cls.pattern_html_search_total))
418+
419+
album_info_list = cls.pattern_html_category_album_info_list.findall(html)
420+
421+
for (album_id, title, tag_text) in album_info_list:
422+
tag_list = cls.pattern_html_search_tag_list.findall(tag_text)
423+
content.append((
424+
album_id, {
425+
'name': title, # 改成name是为了兼容 parse_api_resp_to_page
426+
'tag_list': tag_list
427+
}
428+
))
429+
430+
return JmSearchPage(content, total)
431+
432+
408433
@classmethod
409434
def parse_html_to_favorite_page(cls, html: str) -> JmFavoritePage:
410435
total = int(PatternTool.require_match(

0 commit comments

Comments
 (0)