mirror of
https://github.com/hect0x7/JMComic-Crawler-Python.git
synced 2025-11-04 14:49:43 +08:00
v2.4.14: 更新禁漫APP端搜索接口,修复导出收藏夹插件的一些问题,优化代码。 (#189)
This commit is contained in:
parent
06237f83f9
commit
7d8fb358b7
2
.github/workflows/export_favorites.yml
vendored
2
.github/workflows/export_favorites.yml
vendored
@ -73,7 +73,7 @@ jobs:
|
|||||||
- name: 上传结果
|
- name: 上传结果
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: '收藏夹导出zip'
|
name: '导出的收藏夹'
|
||||||
path: ${{ env.ZIP_FP }}
|
path: ${{ env.ZIP_FP }}
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
retention-days: 90
|
retention-days: 90
|
||||||
|
|||||||
@ -24,4 +24,4 @@ plugins:
|
|||||||
msg_to: ${EMAIL_TO}
|
msg_to: ${EMAIL_TO}
|
||||||
password: ${EMAIL_PASS}
|
password: ${EMAIL_PASS}
|
||||||
title: ${EMAIL_TITLE}
|
title: ${EMAIL_TITLE}
|
||||||
content: ${EMAIL_CONTENT}
|
content: ${EMAIL_CONTENT}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
# 被依赖方 <--- 使用方
|
# 被依赖方 <--- 使用方
|
||||||
# config <--- entity <--- toolkit <--- client <--- option <--- downloader
|
# config <--- entity <--- toolkit <--- client <--- option <--- downloader
|
||||||
|
|
||||||
__version__ = '2.4.13'
|
__version__ = '2.4.14'
|
||||||
|
|
||||||
from .api import *
|
from .api import *
|
||||||
from .jm_plugin import *
|
from .jm_plugin import *
|
||||||
|
|||||||
@ -318,7 +318,7 @@ class JmModuleConfig:
|
|||||||
# 一般情况下,建议使用option配置文件来定制配置
|
# 一般情况下,建议使用option配置文件来定制配置
|
||||||
# 而如果只想修改几个简单常用的配置,也可以下方的DEFAULT_XXX属性
|
# 而如果只想修改几个简单常用的配置,也可以下方的DEFAULT_XXX属性
|
||||||
JM_OPTION_VER = '2.1'
|
JM_OPTION_VER = '2.1'
|
||||||
DEFAULT_CLIENT_IMPL = 'html' # 默认Client实现类型为网页端
|
DEFAULT_CLIENT_IMPL = 'api' # 默认Client实现类型为网页端
|
||||||
DEFAULT_CLIENT_CACHE = True # 默认开启Client缓存,缓存级别是level_option,详见CacheRegistry
|
DEFAULT_CLIENT_CACHE = True # 默认开启Client缓存,缓存级别是level_option,详见CacheRegistry
|
||||||
DEFAULT_PROXIES = ProxyBuilder.system_proxy() # 默认使用系统代理
|
DEFAULT_PROXIES = ProxyBuilder.system_proxy() # 默认使用系统代理
|
||||||
|
|
||||||
@ -345,7 +345,7 @@ class JmModuleConfig:
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'impl': None,
|
'impl': None,
|
||||||
'retry_times': 5
|
'retry_times': 5,
|
||||||
},
|
},
|
||||||
'plugins': {
|
'plugins': {
|
||||||
# 如果插件抛出参数校验异常,只log。(全局配置,可以被插件的局部配置覆盖)
|
# 如果插件抛出参数校验异常,只log。(全局配置,可以被插件的局部配置覆盖)
|
||||||
|
|||||||
@ -524,7 +524,7 @@ class JmPageContent(JmBaseEntity, IndexedEntity):
|
|||||||
"""
|
"""
|
||||||
content:
|
content:
|
||||||
[
|
[
|
||||||
album_id, {title, tag_list, ...}
|
album_id, {title, tags, ...}
|
||||||
]
|
]
|
||||||
:param content: 分页数据
|
:param content: 分页数据
|
||||||
:param total: 总结果数
|
:param total: 总结果数
|
||||||
@ -564,11 +564,11 @@ class JmPageContent(JmBaseEntity, IndexedEntity):
|
|||||||
|
|
||||||
def iter_id_title_tag(self) -> Generator[Tuple[str, str, List[str]], None, None]:
|
def iter_id_title_tag(self) -> Generator[Tuple[str, str, List[str]], None, None]:
|
||||||
"""
|
"""
|
||||||
返回 album_id, album_title, album_tag_list 的迭代器
|
返回 album_id, album_title, album_tags 的迭代器
|
||||||
"""
|
"""
|
||||||
for aid, ainfo in self.content:
|
for aid, ainfo in self.content:
|
||||||
ainfo.setdefault('tag_list', [])
|
ainfo.setdefault('tags', [])
|
||||||
yield aid, ainfo['name'], ainfo['tag_list']
|
yield aid, ainfo['name'], ainfo['tags']
|
||||||
|
|
||||||
# 下面的方法实现方便的元素访问
|
# 下面的方法实现方便的元素访问
|
||||||
|
|
||||||
@ -610,7 +610,7 @@ class JmSearchPage(JmPageContent):
|
|||||||
page = JmSearchPage([(
|
page = JmSearchPage([(
|
||||||
album.album_id, {
|
album.album_id, {
|
||||||
'name': album.name,
|
'name': album.name,
|
||||||
'tag_list': album.tags,
|
'tags': album.tags,
|
||||||
}
|
}
|
||||||
)], 1)
|
)], 1)
|
||||||
setattr(page, 'album', album)
|
setattr(page, 'album', album)
|
||||||
|
|||||||
@ -44,22 +44,17 @@ class JmOptionPlugin:
|
|||||||
msg=msg
|
msg=msg
|
||||||
)
|
)
|
||||||
|
|
||||||
def require_true(self, case: Any, msg: str, is_param_validation=True):
|
def require_param(self, case: Any, msg: str):
|
||||||
"""
|
"""
|
||||||
独立于ExceptionTool的一套异常抛出体系
|
专门用于校验参数的方法,会抛出特定异常,由option拦截根据策略进行处理
|
||||||
|
|
||||||
|
|
||||||
:param case: 条件
|
:param case: 条件
|
||||||
:param msg: 报错信息
|
:param msg: 报错信息
|
||||||
:param is_param_validation: True 表示 调用本方法是用于校验参数,则会抛出特定异常(PluginValidationException)
|
|
||||||
"""
|
"""
|
||||||
if case:
|
if case:
|
||||||
return
|
return
|
||||||
|
|
||||||
if is_param_validation:
|
raise PluginValidationException(self, msg)
|
||||||
raise PluginValidationException(self, msg)
|
|
||||||
else:
|
|
||||||
ExceptionTool.raises(msg)
|
|
||||||
|
|
||||||
def warning_lib_not_install(self, lib: str):
|
def warning_lib_not_install(self, lib: str):
|
||||||
msg = (f'插件`{self.plugin_key}`依赖库: {lib},请先安装{lib}再使用。'
|
msg = (f'插件`{self.plugin_key}`依赖库: {lib},请先安装{lib}再使用。'
|
||||||
@ -94,6 +89,11 @@ class JmOptionPlugin:
|
|||||||
"""
|
"""
|
||||||
return os.system(cmd)
|
return os.system(cmd)
|
||||||
|
|
||||||
|
# noinspection PyMethodMayBeStatic
|
||||||
|
def execute_multi_line_cmd(self, cmd: str):
|
||||||
|
import subprocess
|
||||||
|
subprocess.run(cmd, shell=True, check=True)
|
||||||
|
|
||||||
|
|
||||||
class JmLoginPlugin(JmOptionPlugin):
|
class JmLoginPlugin(JmOptionPlugin):
|
||||||
"""
|
"""
|
||||||
@ -106,10 +106,10 @@ class JmLoginPlugin(JmOptionPlugin):
|
|||||||
password: str,
|
password: str,
|
||||||
impl=None,
|
impl=None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.require_true(username, '用户名不能为空')
|
self.require_param(username, '用户名不能为空')
|
||||||
self.require_true(password, '密码不能为空')
|
self.require_param(password, '密码不能为空')
|
||||||
|
|
||||||
client = self.option.new_jm_client(impl=impl)
|
client = self.option.build_jm_client(impl=impl)
|
||||||
client.login(username, password)
|
client.login(username, password)
|
||||||
|
|
||||||
cookies = dict(client['cookies'])
|
cookies = dict(client['cookies'])
|
||||||
@ -459,7 +459,7 @@ class SendQQEmailPlugin(JmOptionPlugin):
|
|||||||
album=None,
|
album=None,
|
||||||
downloader=None,
|
downloader=None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.require_true(msg_from and msg_to and password, '发件人、收件人、授权码都不能为空')
|
self.require_param(msg_from and msg_to and password, '发件人、收件人、授权码都不能为空')
|
||||||
|
|
||||||
from common import EmailConfig
|
from common import EmailConfig
|
||||||
econfig = EmailConfig(msg_from, msg_to, password)
|
econfig = EmailConfig(msg_from, msg_to, password)
|
||||||
@ -551,7 +551,7 @@ class FavoriteFolderExportPlugin(JmOptionPlugin):
|
|||||||
zip_password=None,
|
zip_password=None,
|
||||||
delete_original_file=False,
|
delete_original_file=False,
|
||||||
):
|
):
|
||||||
self.save_dir = os.path.abspath(save_dir if save_dir is not None else os.getcwd() + '/export/')
|
self.save_dir = os.path.abspath(save_dir if save_dir is not None else (os.getcwd() + '/export/'))
|
||||||
self.zip_enable = zip_enable
|
self.zip_enable = zip_enable
|
||||||
self.zip_filepath = os.path.abspath(zip_filepath)
|
self.zip_filepath = os.path.abspath(zip_filepath)
|
||||||
self.zip_password = zip_password
|
self.zip_password = zip_password
|
||||||
@ -564,7 +564,7 @@ class FavoriteFolderExportPlugin(JmOptionPlugin):
|
|||||||
self.main()
|
self.main()
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
cl = self.option.new_jm_client(impl=JmApiClient)
|
cl = self.option.build_jm_client()
|
||||||
# noinspection PyAttributeOutsideInit
|
# noinspection PyAttributeOutsideInit
|
||||||
self.cl = cl
|
self.cl = cl
|
||||||
page = cl.favorite_folder()
|
page = cl.favorite_folder()
|
||||||
@ -584,7 +584,7 @@ class FavoriteFolderExportPlugin(JmOptionPlugin):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# 压缩导出的文件
|
# 压缩导出的文件
|
||||||
self.require_true(self.zip_filepath, '如果开启zip,请指定zip_filepath参数(压缩文件保存路径)')
|
self.require_param(self.zip_filepath, '如果开启zip,请指定zip_filepath参数(压缩文件保存路径)')
|
||||||
|
|
||||||
if self.zip_password is None:
|
if self.zip_password is None:
|
||||||
self.zip_folder_without_password(self.files, self.zip_filepath)
|
self.zip_folder_without_password(self.files, self.zip_filepath)
|
||||||
@ -650,12 +650,16 @@ class FavoriteFolderExportPlugin(JmOptionPlugin):
|
|||||||
zipf.write(file, arcname=of_file_name(file))
|
zipf.write(file, arcname=of_file_name(file))
|
||||||
|
|
||||||
def zip_with_password(self):
|
def zip_with_password(self):
|
||||||
os.chdir(self.save_dir)
|
# 构造shell命令
|
||||||
cmd = f'7z a "{self.zip_filepath}" "{self.save_dir}" -p{self.zip_password} -mhe=on'
|
cmd_list = f'''
|
||||||
self.require_true(
|
cd {self.save_dir}
|
||||||
0 == self.execute_cmd(cmd),
|
7z a "{self.zip_filepath}" "./" -p{self.zip_password} -mhe=on > "../7z_output.txt"
|
||||||
'加密压缩文件失败'
|
|
||||||
)
|
'''
|
||||||
|
self.log(f'运行命令: {cmd_list}')
|
||||||
|
|
||||||
|
# 执行
|
||||||
|
self.execute_multi_line_cmd(cmd_list)
|
||||||
|
|
||||||
|
|
||||||
class ConvertJpgToPdfPlugin(JmOptionPlugin):
|
class ConvertJpgToPdfPlugin(JmOptionPlugin):
|
||||||
@ -726,11 +730,10 @@ class ConvertJpgToPdfPlugin(JmOptionPlugin):
|
|||||||
self.log(f'Execute Command: [{cmd}]')
|
self.log(f'Execute Command: [{cmd}]')
|
||||||
code = self.execute_cmd(cmd)
|
code = self.execute_cmd(cmd)
|
||||||
|
|
||||||
self.require_true(
|
ExceptionTool.require_true(
|
||||||
code == 0,
|
code == 0,
|
||||||
'jpg图片合并为pdf失败!'
|
'jpg图片合并为pdf失败!'
|
||||||
'请确认你是否安装了magick,安装网站: [http://www.imagemagick.org/]',
|
'请确认你是否安装了magick,安装网站: [http://www.imagemagick.org/]',
|
||||||
False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.log(f'Convert Successfully: JM{photo.id} → {pdf_filepath}')
|
self.log(f'Convert Successfully: JM{photo.id} → {pdf_filepath}')
|
||||||
|
|||||||
@ -371,7 +371,7 @@ class JmPageTool:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 用来查找tag列表
|
# 用来查找tag列表
|
||||||
pattern_html_search_tag_list = compile(r'<a[^>]*?>(.*?)</a>')
|
pattern_html_search_tags = compile(r'<a[^>]*?>(.*?)</a>')
|
||||||
|
|
||||||
# 查找错误,例如 [错误,關鍵字過短,請至少輸入兩個字以上。]
|
# 查找错误,例如 [错误,關鍵字過短,請至少輸入兩個字以上。]
|
||||||
pattern_html_search_error = compile(r'<fieldset>\n<legend>(.*?)</legend>\n<div class=.*?>\n(.*?)\n</div>\n</fieldset>')
|
pattern_html_search_error = compile(r'<fieldset>\n<legend>(.*?)</legend>\n<div class=.*?>\n(.*?)\n</div>\n</fieldset>')
|
||||||
@ -418,11 +418,11 @@ class JmPageTool:
|
|||||||
album_info_list = cls.pattern_html_search_album_info_list.findall(html)
|
album_info_list = cls.pattern_html_search_album_info_list.findall(html)
|
||||||
|
|
||||||
for (album_id, title, _, label_category, label_sub, tag_text) in album_info_list:
|
for (album_id, title, _, label_category, label_sub, tag_text) in album_info_list:
|
||||||
tag_list = cls.pattern_html_search_tag_list.findall(tag_text)
|
tags = cls.pattern_html_search_tags.findall(tag_text)
|
||||||
content.append((
|
content.append((
|
||||||
album_id, {
|
album_id, {
|
||||||
'name': title, # 改成name是为了兼容 parse_api_resp_to_page
|
'name': title, # 改成name是为了兼容 parse_api_resp_to_page
|
||||||
'tag_list': tag_list
|
'tags': tags
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
|
||||||
@ -436,11 +436,11 @@ class JmPageTool:
|
|||||||
album_info_list = cls.pattern_html_category_album_info_list.findall(html)
|
album_info_list = cls.pattern_html_category_album_info_list.findall(html)
|
||||||
|
|
||||||
for (album_id, title, tag_text) in album_info_list:
|
for (album_id, title, tag_text) in album_info_list:
|
||||||
tag_list = cls.pattern_html_search_tag_list.findall(tag_text)
|
tags = cls.pattern_html_search_tags.findall(tag_text)
|
||||||
content.append((
|
content.append((
|
||||||
album_id, {
|
album_id, {
|
||||||
'name': title, # 改成name是为了兼容 parse_api_resp_to_page
|
'name': title, # 改成name是为了兼容 parse_api_resp_to_page
|
||||||
'tag_list': tag_list
|
'tags': tags
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
|
||||||
@ -494,7 +494,7 @@ class JmPageTool:
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
total: int = int(data.total)
|
total: int = int(data.total or 0) # 2024.1.5 data.total可能为None
|
||||||
content = cls.adapt_content(data.content)
|
content = cls.adapt_content(data.content)
|
||||||
return JmSearchPage(content, total)
|
return JmSearchPage(content, total)
|
||||||
|
|
||||||
@ -546,7 +546,7 @@ class JmPageTool:
|
|||||||
def adapt_content(cls, content):
|
def adapt_content(cls, content):
|
||||||
def adapt_item(item: DictModel):
|
def adapt_item(item: DictModel):
|
||||||
item: dict = item.src_dict
|
item: dict = item.src_dict
|
||||||
item.setdefault('tag_list', [])
|
item.setdefault('tags', [])
|
||||||
return item
|
return item
|
||||||
|
|
||||||
content = [
|
content = [
|
||||||
|
|||||||
@ -22,8 +22,8 @@ class Test_Client(JmTestConfigurable):
|
|||||||
for aid, ainfo in page[0:1:1]:
|
for aid, ainfo in page[0:1:1]:
|
||||||
print(aid, ainfo)
|
print(aid, ainfo)
|
||||||
|
|
||||||
for aid, atitle, tag_list in page.iter_id_title_tag():
|
for aid, atitle, tags in page.iter_id_title_tag():
|
||||||
print(aid, atitle, tag_list)
|
print(aid, atitle, tags)
|
||||||
|
|
||||||
aid = '438516'
|
aid = '438516'
|
||||||
page = self.client.search_site(aid)
|
page = self.client.search_site(aid)
|
||||||
|
|||||||
@ -32,6 +32,8 @@ def prepare_actions_input_and_secrets():
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
prepare_actions_input_and_secrets()
|
prepare_actions_input_and_secrets()
|
||||||
|
# 关闭logging,保证安全
|
||||||
|
disable_jm_log()
|
||||||
option = create_option('../assets/option/option_workflow_export_favorites.yml')
|
option = create_option('../assets/option/option_workflow_export_favorites.yml')
|
||||||
option.call_all_plugin('main', safe=False)
|
option.call_all_plugin('main', safe=False)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user