Skip to content

Commit

Permalink
v2.3.0: 初步实现移动端API(JmApiClient),优化正则表达式并适配大陆直连域名,增加测试,优化工作流 (#137)
Browse files Browse the repository at this point in the history
  • Loading branch information
hect0x7 authored Sep 15, 2023
1 parent c5810d9 commit 89deeda
Show file tree
Hide file tree
Showing 16 changed files with 347 additions and 88 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/release_auto.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Release (auto)


on:
workflow_dispatch:

jobs:
release:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: "3.11"

- name: Parse Tag & Body
id: tb
run: |
commit_message=$(git log --format=%B -n 1 ${{ github.sha }})
python release.py "$commit_message"
- name: Create Release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.tb.outputs.tag }}
body: ${{ steps.tb.outputs.body }}

- name: Build Module
run: |
python -m pip install build
python -m build
- name: Release PYPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_JMCOMIC }}
9 changes: 9 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 大版本更新内容计划

| 版本范围 | 更新内容 |
|:--------:|:--------------------------------------:|
| v2.3.* | 实现移动端API的基础功能,统一HTML和API的实现 |
| v2.2.* | 新的插件体系,新的命令行调用,完善搜索功能。 |
| v2.1.* | 拆分Downloader抽象调度,优化可扩展性、代码复用性、模块级别自定义。 |
| v2.0.* | 重新设计合理的抽象层次,实现请求重试切换域名机制,新的option配置设计。 |
| v1.\*.\* | 基于HTML实现基础功能。 |
20 changes: 20 additions & 0 deletions release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import os
import sys
import re


def add_output(k, v):
print(f'set {k} = {v}')
print(os.system(f'echo {k}={v} >> $GITHUB_OUTPUT'))


msg = sys.argv[1]
print(f'msg: {msg}')
p = re.compile('(.*?): ?(.*)')
match = p.search(msg)
assert match is not None, f'commit message format is wrong: {msg}'

tag, body = match[1], match[2]

add_output('tag', tag)
add_output('body', body)
2 changes: 1 addition & 1 deletion src/jmcomic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# 被依赖方 <--- 使用方
# config <--- entity <--- toolkit <--- client <--- option <--- downloader

__version__ = '2.2.11'
__version__ = '2.3.0'

from .api import *
from .jm_plugin import *
Expand Down
6 changes: 3 additions & 3 deletions src/jmcomic/cl.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ def parse_arg(self):
parser.add_argument(
'--option',
help='path to the option file, you can also specify it by env `JM_OPTION_PATH`',
default=get_env('JM_OPTION_PATH', None),
default=get_env('JM_OPTION_PATH', ''),
)

args = parser.parse_args()
if args.option is not None:
if len(args.option) != 0:
self.option_path = os.path.abspath(args.option)
else:
self.option_path = None
Expand Down Expand Up @@ -88,7 +88,7 @@ def main(self):
option = create_option(self.option_path)
else:
option = JmOption.default()

self.run(option)

def run(self, option):
Expand Down
38 changes: 38 additions & 0 deletions src/jmcomic/jm_client_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,16 @@ def check_special_http_code(cls, resp):
class JmApiClient(AbstractJmClient):
client_key = 'api'
API_SEARCH = '/search'
API_ALBUM = '/album'
API_CHAPTER = '/chapter'

def __init__(self,
postman: Postman,
retry_times: int,
domain=None,
fallback_domain_list=None,
):
super().__init__(postman, retry_times, domain, fallback_domain_list)

def search(self,
search_query: str,
Expand Down Expand Up @@ -431,6 +441,30 @@ def search(self,

return JmcomicSearchTool.parse_api_resp_to_page(data)

def get_album_detail(self, album_id) -> JmAlbumDetail:
return self.fetch_detail_entity(album_id,
JmModuleConfig.album_class(),
)

def get_photo_detail(self, photo_id, fetch_album=True) -> JmPhotoDetail:
return self.fetch_detail_entity(photo_id,
JmModuleConfig.photo_class(),
)

def fetch_detail_entity(self, apid, clazz, **kwargs):
url = self.API_ALBUM if issubclass(clazz, JmAlbumDetail) else self.API_CHAPTER
resp = self.get(
url,
params={
'id': JmcomicText.parse_to_album_id(apid),
**kwargs,
}
)

self.require_resp_success(resp)

return JmApiAdaptTool.parse_entity(resp.res_data, clazz)

def get(self, url, **kwargs) -> JmApiResp:
# set headers
headers, key_ts = self.headers_key_ts
Expand All @@ -454,6 +488,10 @@ def headers_key_ts(self):
def debug_topic_request(self):
return 'api'

# noinspection PyMethodMayBeStatic
def require_resp_success(self, resp: JmApiResp):
resp.require_success()


JmModuleConfig.register_client(JmHtmlClient)
JmModuleConfig.register_client(JmApiClient)
14 changes: 7 additions & 7 deletions src/jmcomic/jm_client_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ def __init__(self, resp, key_ts):
self.key_ts = key_ts
self.cache_decode_data = None

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

@staticmethod
def parse_data(text, time) -> str:
# 1. base64解码
Expand All @@ -102,11 +106,9 @@ def parse_data(text, time) -> str:
return res

@property
@field_cache('__cache_decoded_data__')
def decoded_data(self) -> str:
if self.cache_decode_data is None:
self.cache_decode_data = self.parse_data(self.encoded_data, self.key_ts)

return self.cache_decode_data
return self.parse_data(self.encoded_data, self.key_ts)

@property
def encoded_data(self) -> str:
Expand All @@ -118,6 +120,7 @@ def res_data(self) -> Any:
from json import loads
return loads(self.decoded_data)

@field_cache('__cache_json__')
def json(self, **kwargs) -> Dict:
return self.resp.json()

Expand All @@ -136,9 +139,6 @@ def is_success(self) -> bool:
def json(self, **kwargs) -> Dict:
return self.resp.json()

def model(self) -> DictModel:
return DictModel(self.json())


"""
Expand Down
10 changes: 5 additions & 5 deletions src/jmcomic/jm_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ class JmModuleConfig:
CLASS_CLIENT_IMPL = {}
CLASS_EXCEPTION = None

# 插件注册表
PLUGIN_REGISTRY = {}

# 执行debug的函数
debug_executor = default_jm_debug
# postman构造函数
Expand All @@ -80,9 +83,6 @@ class JmModuleConfig:
# debug时解码url
decode_url_when_debug = True

# 插件注册表
plugin_registry = {}

@classmethod
def downloader_class(cls):
if cls.CLASS_DOWNLOADER is not None:
Expand Down Expand Up @@ -243,7 +243,7 @@ def new_postman(cls, session=False, **kwargs):
default_option_dict: dict = {
'version': '2.0',
'debug': None,
'dir_rule': {'rule': 'Bd_Ptitle', 'base_dir': None},
'dir_rule': {'rule': 'Bd_Pname', 'base_dir': None},
'download': {
'cache': True,
'image': {'decode': True, 'suffix': None},
Expand Down Expand Up @@ -299,7 +299,7 @@ def option_default_dict(cls) -> dict:

@classmethod
def register_plugin(cls, plugin_class):
cls.plugin_registry[plugin_class.plugin_key] = plugin_class
cls.PLUGIN_REGISTRY[plugin_class.plugin_key] = plugin_class

@classmethod
def register_client(cls, client_class):
Expand Down
6 changes: 3 additions & 3 deletions src/jmcomic/jm_downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ def before_album(self, album: JmAlbumDetail):
f'作者: [{album.author}], '
f'章节数: [{len(album)}], '
f'总页数: [{album.page_count}], '
f'标题: [{album.title}], '
f'关键词: [{album.tag_list}]'
f'标题: [{album.name}], '
f'关键词: [{album.tags}]'
)

def after_album(self, album: JmAlbumDetail):
Expand All @@ -32,7 +32,7 @@ def after_album(self, album: JmAlbumDetail):
def before_photo(self, photo: JmPhotoDetail):
jm_debug('photo.before',
f'开始下载章节: {photo.id} ({photo.album_id}[{photo.index}/{len(photo.from_album)}]), '
f'标题: [{photo.title}], '
f'标题: [{photo.name}], '
f'图片数为[{len(photo)}]'
)

Expand Down
Loading

0 comments on commit 89deeda

Please sign in to comment.