Skip to content

Commit af3ead1

Browse files
committed
重构插件的文件生成路径规则 (#437)
1 parent 905f72c commit af3ead1

3 files changed

Lines changed: 61 additions & 149 deletions

File tree

src/jmcomic/jm_config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ def disable_jm_log(cls):
380380

381381
@classmethod
382382
def new_postman(cls, session=False, **kwargs):
383-
kwargs.setdefault('impersonate', 'chrome110')
383+
kwargs.setdefault('impersonate', 'chrome')
384384
kwargs.setdefault('headers', JmModuleConfig.new_html_headers())
385385
kwargs.setdefault('proxies', JmModuleConfig.DEFAULT_PROXIES)
386386

@@ -416,7 +416,7 @@ def new_postman(cls, session=False, **kwargs):
416416
'postman': {
417417
'type': 'curl_cffi',
418418
'meta_data': {
419-
'impersonate': 'chrome110',
419+
'impersonate': 'chrome',
420420
'headers': None,
421421
'proxies': None,
422422
}

src/jmcomic/jm_option.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,12 @@ def decide_image_save_dir(self,
7070
album: JmAlbumDetail,
7171
photo: JmPhotoDetail,
7272
) -> str:
73-
return self._build_path_from_rules(album, photo)
73+
return self.apply_rule_to_path(album, photo)
7474

7575
def decide_album_root_dir(self, album: JmAlbumDetail) -> str:
76-
return self._build_path_from_rules(album, None, True)
76+
return self.apply_rule_to_path(album, None, True)
7777

78-
def _build_path_from_rules(self, album, photo, only_album_rules=False) -> str:
78+
def apply_rule_to_path(self, album, photo, only_album_rules=False) -> str:
7979
path_ls = []
8080
for rule, parser in self.parser_list:
8181
if only_album_rules and not (rule == self.RULE_BASE_DIR or rule.startswith('A')):
@@ -92,7 +92,7 @@ def _build_path_from_rules(self, album, photo, only_album_rules=False) -> str:
9292

9393
path_ls.append(path)
9494

95-
return fix_filepath('/'.join(path_ls), is_dir=True)
95+
return fix_filepath('/'.join(path_ls))
9696

9797
def get_rule_parser_list(self, rule_dsl: str):
9898
"""
@@ -103,7 +103,6 @@ def get_rule_parser_list(self, rule_dsl: str):
103103
parser_list: list = []
104104

105105
for rule in rule_list:
106-
rule = rule.strip()
107106
if rule == self.RULE_BASE_DIR:
108107
parser_list.append((rule, self.parse_bd_rule))
109108
continue
@@ -135,17 +134,21 @@ def parse_detail_rule(cls, album, photo, rule: str):
135134
return str(DetailEntity.get_dirname(detail, rule[1:]))
136135

137136
# noinspection PyMethodMayBeStatic
138-
def split_rule_dsl(self, rule_dsl: str):
139-
if rule_dsl == self.RULE_BASE_DIR:
140-
return [rule_dsl]
141-
137+
def split_rule_dsl(self, rule_dsl: str) -> list[str]:
142138
if '/' in rule_dsl:
143-
return rule_dsl.split('/')
139+
rule_list = rule_dsl.split('/')
140+
elif '_' in rule_dsl:
141+
rule_list = rule_dsl.split('_')
142+
else:
143+
rule_list = [rule_dsl]
144+
145+
for i, e in enumerate(rule_list):
146+
rule_list[i] = e.strip()
144147

145-
if '_' in rule_dsl:
146-
return rule_dsl.split('_')
148+
if rule_list[0] != self.RULE_BASE_DIR:
149+
rule_list.insert(0, self.RULE_BASE_DIR)
147150

148-
ExceptionTool.raises(f'不支持的rule配置: "{rule_dsl}"')
151+
return rule_list
149152

150153
@classmethod
151154
def get_rule_parser(cls, rule: str):
@@ -158,7 +161,7 @@ def get_rule_parser(cls, rule: str):
158161
ExceptionTool.raises(f'不支持的rule配置: "{rule}"')
159162

160163
@classmethod
161-
def apply_rule_directly(cls, album, photo, rule: str) -> str:
164+
def apply_rule_to_filename(cls, album, photo, rule: str) -> str:
162165
if album is None:
163166
album = photo.from_album
164167
# noinspection PyArgumentList

src/jmcomic/jm_plugin.py

Lines changed: 42 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""
22
该文件存放的是option插件
33
"""
4-
import os.path
54

65
from .jm_option import *
76

@@ -76,6 +75,9 @@ def execute_deletion(self, paths: List[str]):
7675
continue
7776

7877
if os.path.isdir(p):
78+
if os.listdir(p):
79+
self.log(f'文件夹中存在非本次下载的文件,请手动删除文件夹内的文件: {p}', 'remove.ignore')
80+
continue
7981
os.rmdir(p)
8082
self.log(f'删除文件夹: {p}', 'remove')
8183
else:
@@ -104,6 +106,33 @@ def leave_wait_list(self):
104106
def wait_until_finish(self):
105107
pass
106108

109+
# noinspection PyMethodMayBeStatic
110+
def decide_filepath(self,
111+
album: Optional[JmAlbumDetail],
112+
photo: Optional[JmPhotoDetail],
113+
filename_rule: str, suffix: str, base_dir: Optional[str],
114+
dir_rule_dict: Optional[dict]
115+
):
116+
"""
117+
根据规则计算一个文件的全路径
118+
119+
参数 dir_rule_dict 优先级最高,
120+
如果 dir_rule_dict 不为空,优先用 dir_rule_dict
121+
否则使用 base_dir + filename_rule + suffix
122+
"""
123+
filepath: str
124+
base_dir: str
125+
if dir_rule_dict is not None:
126+
dir_rule = DirRule(**dir_rule_dict)
127+
filepath = dir_rule.apply_rule_to_path(album, photo)
128+
base_dir = os.path.dirname(filepath)
129+
else:
130+
base_dir = base_dir or os.getcwd()
131+
filepath = os.path.join(base_dir, DirRule.apply_rule_to_filename(album, photo, filename_rule) + fix_suffix(suffix))
132+
133+
mkdir_if_not_exists(base_dir)
134+
return filepath
135+
107136

108137
class JmLoginPlugin(JmOptionPlugin):
109138
"""
@@ -285,7 +314,8 @@ def invoke(self,
285314
level='photo',
286315
filename_rule='Ptitle',
287316
suffix='zip',
288-
zip_dir='./'
317+
zip_dir='./',
318+
dir_rule=None,
289319
) -> None:
290320

291321
from .jm_downloader import JmDownloader
@@ -302,12 +332,12 @@ def invoke(self,
302332
photo_dict = self.get_downloaded_photo(downloader, album, photo)
303333

304334
if level == 'album':
305-
zip_path = self.get_zip_path(album, None, filename_rule, suffix, zip_dir)
335+
zip_path = self.decide_filepath(album, None, filename_rule, suffix, zip_dir, dir_rule)
306336
self.zip_album(album, photo_dict, zip_path, path_to_delete)
307337

308338
elif level == 'photo':
309339
for photo, image_list in photo_dict.items():
310-
zip_path = self.get_zip_path(photo.from_album, photo, filename_rule, suffix, zip_dir)
340+
zip_path = self.decide_filepath(photo.from_album, photo, filename_rule, suffix, zip_dir, dir_rule)
311341
self.zip_photo(photo, image_list, zip_path, path_to_delete)
312342

313343
else:
@@ -371,18 +401,6 @@ def after_zip(self, path_to_delete: List[str]):
371401
self.execute_deletion(image_paths)
372402
self.execute_deletion(dirs)
373403

374-
# noinspection PyMethodMayBeStatic
375-
def get_zip_path(self, album, photo, filename_rule, suffix, zip_dir):
376-
"""
377-
计算zip文件的路径
378-
"""
379-
filename = DirRule.apply_rule_directly(album, photo, filename_rule)
380-
from os.path import join
381-
return join(
382-
zip_dir,
383-
filename + fix_suffix(suffix),
384-
)
385-
386404

387405
class ClientProxyPlugin(JmOptionPlugin):
388406
plugin_key = 'client_proxy'
@@ -649,95 +667,6 @@ def zip_with_password(self):
649667
self.execute_multi_line_cmd(cmd_list)
650668

651669

652-
class ConvertJpgToPdfPlugin(JmOptionPlugin):
653-
plugin_key = 'j2p'
654-
655-
def check_image_suffix_is_valid(self, std_suffix):
656-
"""
657-
检查option配置的图片后缀转换,目前限制使用Magick时只能搭配jpg
658-
暂不探究Magick是否支持更多图片格式
659-
"""
660-
cur_suffix: Optional[str] = self.option.download.image.suffix
661-
662-
ExceptionTool.require_true(
663-
cur_suffix is not None and cur_suffix.endswith(std_suffix),
664-
'请把图片的后缀转换配置为jpg,不然无法使用Magick!'
665-
f'(当前配置是[{cur_suffix}])\n'
666-
f'配置模板如下: \n'
667-
f'```\n'
668-
f'download:\n'
669-
f' image:\n'
670-
f' suffix: {std_suffix} # 当前配置是{cur_suffix}\n'
671-
f'```'
672-
)
673-
674-
def invoke(self,
675-
photo: JmPhotoDetail,
676-
downloader=None,
677-
pdf_dir=None,
678-
filename_rule='Pid',
679-
quality=100,
680-
delete_original_file=False,
681-
override_cmd=None,
682-
override_jpg=None,
683-
**kwargs,
684-
):
685-
self.delete_original_file = delete_original_file
686-
687-
# 检查图片后缀配置
688-
suffix = override_jpg or '.jpg'
689-
self.check_image_suffix_is_valid(suffix)
690-
691-
# 处理文件夹配置
692-
filename = DirRule.apply_rule_directly(None, photo, filename_rule)
693-
photo_dir = self.option.decide_image_save_dir(photo)
694-
695-
# 处理生成的pdf文件的路径
696-
if pdf_dir is None:
697-
pdf_dir = photo_dir
698-
else:
699-
pdf_dir = fix_filepath(pdf_dir, True)
700-
mkdir_if_not_exists(pdf_dir)
701-
702-
pdf_filepath = os.path.join(pdf_dir, f'{filename}.pdf')
703-
704-
# 生成命令
705-
def generate_cmd():
706-
return (
707-
override_cmd or
708-
'magick convert -quality {quality} "{photo_dir}*{suffix}" "{pdf_filepath}"'
709-
).format(
710-
quality=quality,
711-
photo_dir=photo_dir,
712-
suffix=suffix,
713-
pdf_filepath=pdf_filepath,
714-
)
715-
716-
cmd = generate_cmd()
717-
self.log(f'Execute Command: [{cmd}]')
718-
code = self.execute_cmd(cmd)
719-
720-
ExceptionTool.require_true(
721-
code == 0,
722-
'jpg图片合并为pdf失败!'
723-
'请确认你是否安装了magick,安装网站: [https://www.imagemagick.org/]',
724-
)
725-
726-
self.log(f'Convert Successfully: JM{photo.id}{pdf_filepath}')
727-
728-
if downloader is not None:
729-
from .jm_downloader import JmDownloader
730-
downloader: JmDownloader
731-
732-
paths = [
733-
path
734-
for path, image in downloader.download_success_dict[photo.from_album][photo]
735-
]
736-
737-
paths.append(self.option.decide_image_save_dir(photo, ensure_exists=False))
738-
self.execute_deletion(paths)
739-
740-
741670
class Img2pdfPlugin(JmOptionPlugin):
742671
plugin_key = 'img2pdf'
743672

@@ -747,6 +676,7 @@ def invoke(self,
747676
downloader=None,
748677
pdf_dir=None,
749678
filename_rule='Pid',
679+
dir_rule=None,
750680
delete_original_file=False,
751681
**kwargs,
752682
):
@@ -762,13 +692,7 @@ def invoke(self,
762692
self.delete_original_file = delete_original_file
763693

764694
# 处理生成的pdf文件的路径
765-
pdf_dir = self.ensure_make_pdf_dir(pdf_dir)
766-
767-
# 处理pdf文件名
768-
filename = DirRule.apply_rule_directly(album, photo, filename_rule)
769-
770-
# pdf路径
771-
pdf_filepath = os.path.join(pdf_dir, f'{filename}.pdf')
695+
pdf_filepath = self.decide_filepath(album, photo, filename_rule, 'pdf', pdf_dir, dir_rule)
772696

773697
# 调用 img2pdf 把 photo_dir 下的所有图片转为pdf
774698
img_path_ls, img_dir_ls = self.write_img_2_pdf(pdf_filepath, album, photo)
@@ -794,18 +718,14 @@ def write_img_2_pdf(self, pdf_filepath, album: JmAlbumDetail, photo: JmPhotoDeta
794718
continue
795719
img_path_ls += imgs
796720

721+
if len(img_path_ls) == 0:
722+
self.log(f'所有文件夹都不存在图片,无法生成pdf:{img_dir_ls}', 'error')
723+
797724
with open(pdf_filepath, 'wb') as f:
798725
f.write(img2pdf.convert(img_path_ls))
799726

800727
return img_path_ls, img_dir_ls
801728

802-
@staticmethod
803-
def ensure_make_pdf_dir(pdf_dir: str):
804-
pdf_dir = pdf_dir or os.getcwd()
805-
pdf_dir = fix_filepath(pdf_dir, True)
806-
mkdir_if_not_exists(pdf_dir)
807-
return pdf_dir
808-
809729

810730
class LongImgPlugin(JmOptionPlugin):
811731
plugin_key = 'long_img'
@@ -817,6 +737,7 @@ def invoke(self,
817737
img_dir=None,
818738
filename_rule='Pid',
819739
delete_original_file=False,
740+
dir_rule=None,
820741
**kwargs,
821742
):
822743
if photo is None and album is None:
@@ -830,14 +751,8 @@ def invoke(self,
830751

831752
self.delete_original_file = delete_original_file
832753

833-
# 处理文件夹配置
834-
img_dir = self.get_img_dir(img_dir)
835-
836754
# 处理生成的长图文件的路径
837-
filename = DirRule.apply_rule_directly(album, photo, filename_rule)
838-
839-
# 长图路径
840-
long_img_path = os.path.join(img_dir, f'{filename}.png')
755+
long_img_path = self.decide_filepath(album, photo, filename_rule, 'png', img_dir, dir_rule)
841756

842757
# 调用 PIL 把 photo_dir 下的所有图片合并为长图
843758
img_path_ls = self.write_img_2_long_img(long_img_path, album, photo)
@@ -856,7 +771,7 @@ def write_img_2_long_img(self, long_img_path, album: JmAlbumDetail, photo: JmPho
856771
img_dir_items = [self.option.decide_image_save_dir(photo) for photo in album]
857772

858773
img_paths = itertools.chain(*map(files_of_dir, img_dir_items))
859-
img_paths = filter(lambda x: not x.startswith('.'), img_paths) # 过滤系统文件
774+
img_paths = list(filter(lambda x: not x.startswith('.'), img_paths)) # 过滤系统文件
860775

861776
images = self.open_images(img_paths)
862777

@@ -895,12 +810,6 @@ def open_images(self, img_paths: List[str]):
895810
self.log(f"Failed to open image {img_path}: {e}", 'error')
896811
return images
897812

898-
@staticmethod
899-
def get_img_dir(img_dir: Optional[str]) -> str:
900-
img_dir = fix_filepath(img_dir or os.getcwd())
901-
mkdir_if_not_exists(img_dir)
902-
return img_dir
903-
904813

905814
class JmServerPlugin(JmOptionPlugin):
906815
plugin_key = 'jm_server'

0 commit comments

Comments
 (0)