From ee23037f94f29ce780866a6f92172f13b8778c51 Mon Sep 17 00:00:00 2001 From: hect0x7 <93357912+hect0x7@users.noreply.github.com> Date: Sat, 16 Mar 2024 16:30:49 +0800 Subject: [PATCH] =?UTF-8?q?v2.5.8:=20=E6=96=B0=E5=A2=9E=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E3=80=90=E8=AE=A2=E9=98=85=E6=9C=AC=E5=AD=90=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E3=80=91=EF=BC=8C=E5=8F=AF=E4=BB=A5=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E8=AE=A2=E9=98=85=E6=9C=AC=E5=AD=90=E7=9A=84=E7=AB=A0?= =?UTF-8?q?=E8=8A=82=E6=9B=B4=E6=96=B0=EF=BC=8C=E5=B9=B6=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E5=92=8C=E5=8F=91=E9=80=81=E9=82=AE=E4=BB=B6=E9=80=9A=E7=9F=A5?= =?UTF-8?q?;=20=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E3=80=82=20(#216)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/docs/sources/option_file_syntax.md | 17 ++++++- src/jmcomic/__init__.py | 2 +- src/jmcomic/jm_client_interface.py | 8 +-- src/jmcomic/jm_exception.py | 2 + src/jmcomic/jm_option.py | 7 +-- src/jmcomic/jm_plugin.py | 60 +++++++++++++++++++++++ src/jmcomic/jm_toolkit.py | 10 ++-- 7 files changed, 92 insertions(+), 14 deletions(-) diff --git a/assets/docs/sources/option_file_syntax.md b/assets/docs/sources/option_file_syntax.md index 568fe02c..d9fbee8c 100644 --- a/assets/docs/sources/option_file_syntax.md +++ b/assets/docs/sources/option_file_syntax.md @@ -7,6 +7,8 @@ 因此,在配置option时,不需要配置全部的值,只需要配置特定部分即可。 * 你可以使用下面的代码来得到option的默认值,你可以删除其中的大部分配置项,只保留你要覆盖的配置项 +* **下面的插件配置,kwargs参数支持引用环境变量,语法为 ${环境变量名}** + ```python from jmcomic import JmOption JmOption.default().to_file('./option.yml') # 创建默认option,导出为option.yml文件 @@ -84,6 +86,8 @@ download: # 文件夹规则配置,决定图片文件存放在你的电脑上的哪个文件夹 dir_rule: # base_dir: 根目录。 + # 此配置也支持引用环境变量,例如 + # base_dir: ${JM_DIR}/下载文件夹/ base_dir: D:/a/b/c/ # rule: 规则dsl。 @@ -106,7 +110,6 @@ dir_rule: ```yml # 插件的配置示例 -# 当kwargs的值为字符串类型时,支持使用环境变量,语法为 ${环境变量名} plugins: after_init: - plugin: usage_log # 实时打印硬件占用率的插件 @@ -160,6 +163,18 @@ plugins: bg.jpg: D:/浏览器的背景图 m_bg.jpeg: D:/移动设备浏览器的背景图 + - plugin: subscribe_album_update # 自动订阅本子并下载、发送邮件通知的插件 + kwargs: + download_if_has_update: true + email_notify: # 见下【发送qq邮件插件】 + msg_from: x + msg_to: x + password: x + title: album update !!! + content: album update !!! + album_photo_dict: + 324930: 424507 + after_album: - plugin: zip # 压缩文件插件 kwargs: diff --git a/src/jmcomic/__init__.py b/src/jmcomic/__init__.py index db1b9f97..2a21021b 100644 --- a/src/jmcomic/__init__.py +++ b/src/jmcomic/__init__.py @@ -2,7 +2,7 @@ # 被依赖方 <--- 使用方 # config <--- entity <--- toolkit <--- client <--- option <--- downloader -__version__ = '2.5.7' +__version__ = '2.5.8' from .api import * from .jm_plugin import * diff --git a/src/jmcomic/jm_client_interface.py b/src/jmcomic/jm_client_interface.py index e9a0149b..a641e747 100644 --- a/src/jmcomic/jm_client_interface.py +++ b/src/jmcomic/jm_client_interface.py @@ -88,8 +88,8 @@ def json(self) -> Dict: except Exception as e: ExceptionTool.raises_resp(f'json解析失败: {e}', self, JsonResolveFailException) - def model(self) -> AdvancedEasyAccessDict: - return AdvancedEasyAccessDict(self.json()) + def model(self) -> AdvancedDict: + return AdvancedDict(self.json()) class JmApiResp(JmJsonResp): @@ -118,9 +118,9 @@ def res_data(self) -> Any: return loads(self.decoded_data) @property - def model_data(self) -> AdvancedEasyAccessDict: + def model_data(self) -> AdvancedDict: self.require_success() - return AdvancedEasyAccessDict(self.res_data) + return AdvancedDict(self.res_data) # album-comment diff --git a/src/jmcomic/jm_exception.py b/src/jmcomic/jm_exception.py index 721fa756..3752d7ea 100644 --- a/src/jmcomic/jm_exception.py +++ b/src/jmcomic/jm_exception.py @@ -12,6 +12,8 @@ def __init__(self, msg: str, context: dict): def from_context(self, key): return self.context[key] + def __str__(self): + return self.msg class ResponseUnexpectedException(JmcomicException): description = '响应不符合预期异常' diff --git a/src/jmcomic/jm_option.py b/src/jmcomic/jm_option.py index d2457e13..90b35e19 100644 --- a/src/jmcomic/jm_option.py +++ b/src/jmcomic/jm_option.py @@ -197,11 +197,11 @@ def __init__(self, # 路径规则配置 self.dir_rule = DirRule(**dir_rule) # 客户端配置 - self.client = AdvancedEasyAccessDict(client) + self.client = AdvancedDict(client) # 下载配置 - self.download = AdvancedEasyAccessDict(download) + self.download = AdvancedDict(download) # 插件配置 - self.plugins = AdvancedEasyAccessDict(plugins) + self.plugins = AdvancedDict(plugins) # 其他配置 self.filepath = filepath @@ -376,6 +376,7 @@ def deconstruct(self) -> Dict: @classmethod def from_file(cls, filepath: str) -> 'JmOption': dic: dict = PackerUtil.unpack(filepath)[0] + dic.setdefault('filepath', filepath) return cls.construct(dic) def to_file(self, filepath=None): diff --git a/src/jmcomic/jm_plugin.py b/src/jmcomic/jm_plugin.py index 4d1c49fe..54dc7b37 100644 --- a/src/jmcomic/jm_plugin.py +++ b/src/jmcomic/jm_plugin.py @@ -903,3 +903,63 @@ def build(cls, option: JmOption) -> 'JmOptionPlugin': instance = JmServerPlugin(option) setattr(cls, field_name, instance) return instance + + +class SubscribeAlbumUpdatePlugin(JmOptionPlugin): + plugin_key = 'subscribe_album_update' + + def invoke(self, + album_photo_dict=None, + email_notify=None, + download_if_has_update=True, + auto_update_after_download=True, + ) -> None: + if album_photo_dict is None: + return + + album_photo_dict: Dict + for album_id, photo_id in album_photo_dict.copy().items(): + # check update + try: + has_update, photo_new_list = self.check_photo_update(album_id, photo_id) + except JmcomicException as e: + self.log('Exception happened: ' + str(e), 'check_update.error') + continue + + if has_update is False: + continue + + self.log(f'album={album_id},发现新章节: {photo_new_list},准备开始下载') + + # send email + try: + if email_notify: + SendQQEmailPlugin.build(self.option).invoke(**email_notify) + except PluginValidationException: + # ignore + pass + + # download new photo + if has_update and download_if_has_update: + self.option.download_photo(photo_new_list) + + if auto_update_after_download: + album_photo_dict[album_id] = photo_new_list[-1] + self.option.to_file() + + def check_photo_update(self, album_id: str, photo_id: str): + client = self.option.new_jm_client() + album = client.get_album_detail(album_id) + + photo_new_list = [] + is_new_photo = False + sentinel = int(photo_id) + + for photo in album: + if is_new_photo: + photo_new_list.append(photo.photo_id) + + if int(photo.photo_id) == sentinel: + is_new_photo = True + + return len(photo_new_list) != 0, photo_new_list diff --git a/src/jmcomic/jm_toolkit.py b/src/jmcomic/jm_toolkit.py index 849534a4..8be3c803 100644 --- a/src/jmcomic/jm_toolkit.py +++ b/src/jmcomic/jm_toolkit.py @@ -472,7 +472,7 @@ def parse_html_to_favorite_page(cls, html: str) -> JmFavoritePage: return JmFavoritePage(content, folder_list, total) @classmethod - def parse_api_to_search_page(cls, data: AdvancedEasyAccessDict) -> JmSearchPage: + def parse_api_to_search_page(cls, data: AdvancedDict) -> JmSearchPage: """ model_data: { "search_query": "MANA", @@ -501,7 +501,7 @@ def parse_api_to_search_page(cls, data: AdvancedEasyAccessDict) -> JmSearchPage: return JmSearchPage(content, total) @classmethod - def parse_api_to_favorite_page(cls, data: AdvancedEasyAccessDict) -> JmFavoritePage: + def parse_api_to_favorite_page(cls, data: AdvancedDict) -> JmFavoritePage: """ { "list": [ @@ -546,7 +546,7 @@ def parse_api_to_favorite_page(cls, data: AdvancedEasyAccessDict) -> JmFavoriteP @classmethod def adapt_content(cls, content): - def adapt_item(item: AdvancedEasyAccessDict): + def adapt_item(item: AdvancedDict): item: dict = item.src_dict item.setdefault('tags', []) return item @@ -673,7 +673,7 @@ def post_adapt_album(cls, data: dict, _clazz: type, fields: dict): series = data['series'] episode_list = [] for chapter in series: - chapter = AdvancedEasyAccessDict(chapter) + chapter = AdvancedDict(chapter) # photo_id, photo_index, photo_title, photo_pub_date episode_list.append( (chapter.id, chapter.sort, chapter.name, None) @@ -688,7 +688,7 @@ def post_adapt_photo(cls, data: dict, _clazz: type, fields: dict): sort = 1 series: list = data['series'] # series中的sort从1开始 for chapter in series: - chapter = AdvancedEasyAccessDict(chapter) + chapter = AdvancedDict(chapter) if int(chapter.id) == int(data['id']): sort = chapter.sort break