Skip to content

Commit c8d33ee

Browse files
authored
New Plugin: EncryptedZipPlugin 负责下载后生成加密压缩包 (#375)
1 parent 8331a16 commit c8d33ee

1 file changed

Lines changed: 162 additions & 0 deletions

File tree

src/jmcomic/jm_plugin.py

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,168 @@ def get_zip_path(self, album, photo, filename_rule, suffix, zip_dir):
383383
filename + fix_suffix(suffix),
384384
)
385385

386+
# Plugin: EncryptedZipPlugin
387+
# Author: AXIS5 a.k.a AXIS5Hacker
388+
class EncryptedZipPlugin(JmOptionPlugin):
389+
plugin_key = 'encryptzip'
390+
391+
# noinspection PyAttributeOutsideInit
392+
def invoke(self,
393+
downloader,
394+
album: JmAlbumDetail = None,
395+
photo: JmPhotoDetail = None,
396+
delete_original_file=False,
397+
level='photo',
398+
filename_rule='Ptitle',
399+
suffix='zip',
400+
zip_dir='./',
401+
default_password=None
402+
) -> None:
403+
# default_password为指定的固定密码,在yml中配置,为空则为随机
404+
405+
downloader: JmDownloader
406+
self.downloader = downloader
407+
self.level = level
408+
self.delete_original_file = delete_original_file
409+
self.default_password=default_password
410+
411+
# 使用的库
412+
import random
413+
import pyzipper
414+
import zipfile
415+
416+
# 确保压缩文件所在文件夹存在
417+
zip_dir = JmcomicText.parse_to_abspath(zip_dir)
418+
mkdir_if_not_exists(zip_dir)
419+
420+
path_to_delete = []
421+
photo_dict = self.get_downloaded_photo(downloader, album, photo)
422+
423+
if level == 'album':
424+
zip_path = self.get_zip_path(album, None, filename_rule, suffix, zip_dir)
425+
self.zip_album(album, photo_dict, zip_path, path_to_delete)
426+
427+
elif level == 'photo':
428+
for photo, image_list in photo_dict.items():
429+
zip_path = self.get_zip_path(None, photo, filename_rule, suffix, zip_dir)
430+
self.zip_photo(photo, image_list, zip_path, path_to_delete)
431+
432+
else:
433+
ExceptionTool.raises(f'Not Implemented Zip Level: {level}')
434+
435+
self.after_zip(path_to_delete)
436+
437+
def generate_random_str(self, randomlength=32):
438+
"""
439+
自动生成随机字符密码,长度由randomlength指定
440+
"""
441+
random_str = ''
442+
base_str = r'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
443+
length = len(base_str) - 1
444+
for i in range(randomlength):
445+
random_str += base_str[random.randint(0, length)]
446+
return random_str
447+
448+
def get_downloaded_photo(self, downloader, album, photo):
449+
return (
450+
downloader.download_success_dict[album]
451+
if album is not None # after_album
452+
else downloader.download_success_dict[photo.from_album] # after_photo
453+
)
454+
455+
def zip_photo(self, photo, image_list: list, zip_path: str, path_to_delete):
456+
"""
457+
压缩photo文件夹
458+
"""
459+
photo_dir = self.option.decide_image_save_dir(photo) \
460+
if len(image_list) == 0 \
461+
else os.path.dirname(image_list[0][0])
462+
463+
#from common import backup_dir_to_zip
464+
#backup_dir_to_zip(photo_dir, zip_path)
465+
if self.default_password is None:
466+
#generate random password
467+
crypt=self.generate_random_str(48)
468+
else:
469+
#fixed password
470+
crypt=self.default_password
471+
zip_file=pyzipper.AESZipFile(zip_path, "w",pyzipper.ZIP_DEFLATED)
472+
473+
if self.default_password is None:
474+
#attach the random password to the comment of the zip file
475+
zip_file.comment=str.encode(crypt)
476+
zip_file.setpassword(str.encode(crypt))
477+
zip_file.setencryption(pyzipper.WZ_AES, nbits=128)
478+
for file in files_of_dir(photo_dir):
479+
abspath = os.path.join(photo_dir, file)
480+
relpath = os.path.relpath(abspath, photo_dir)
481+
zip_file.write(abspath, relpath)
482+
if self.default_password is not None:
483+
self.log(f'指定固定密码[{self.default_password}]')
484+
self.log(f'加密压缩章节[{photo.photo_id}]成功 → {zip_path}', 'finish')
485+
path_to_delete.append(self.unified_path(photo_dir))
486+
487+
@staticmethod
488+
def unified_path(f):
489+
return fix_filepath(f, os.path.isdir(f))
490+
491+
def zip_album(self, album, photo_dict: dict, zip_path, path_to_delete):
492+
"""
493+
压缩album文件夹
494+
"""
495+
496+
album_dir = self.option.dir_rule.decide_album_root_dir(album)
497+
498+
with pyzipper.AESZipFile(zip_path, 'w', pyzipper.ZIP_DEFLATED) as f:
499+
if self.default_password is None:
500+
#generate random password
501+
crypt=self.generate_random_str(48)
502+
else:
503+
#fixed password
504+
crypt=self.default_password
505+
if self.default_password is None:
506+
#attach the random password to the comment of the zip file
507+
zip_file.comment=str.encode(crypt)
508+
f.setpassword(str.encode(crypt))
509+
f.setencryption(pyzipper.WZ_AES, nbits=128)
510+
for photo in photo_dict.keys():
511+
# 定位到章节所在文件夹
512+
photo_dir = self.unified_path(self.option.decide_image_save_dir(photo))
513+
# 章节文件夹标记为删除
514+
path_to_delete.append(photo_dir)
515+
for file in files_of_dir(photo_dir):
516+
abspath = os.path.join(photo_dir, file)
517+
relpath = os.path.relpath(abspath, album_dir)
518+
f.write(abspath, relpath)
519+
if self.default_password is not None:
520+
self.log(f'指定固定密码[{self.default_password}]')
521+
self.log(f'加密压缩本子[{album.album_id}]成功 → {zip_path}', 'finish')
522+
523+
def after_zip(self, path_to_delete: List[str]):
524+
# 删除所有原文件
525+
dirs = sorted(path_to_delete, reverse=True)
526+
527+
image_paths = [
528+
path
529+
for photo_dict in self.downloader.download_success_dict.values()
530+
for image_list in photo_dict.values()
531+
for path, image in image_list
532+
]
533+
#print(image_paths)
534+
self.execute_deletion(image_paths)
535+
self.execute_deletion(dirs)
536+
537+
# noinspection PyMethodMayBeStatic
538+
def get_zip_path(self, album, photo, filename_rule, suffix, zip_dir):
539+
"""
540+
计算zip文件的路径
541+
"""
542+
filename = DirRule.apply_rule_directly(album, photo, filename_rule)
543+
from os.path import join
544+
return join(
545+
zip_dir,
546+
filename + fix_suffix(suffix),
547+
)
386548

387549
class ClientProxyPlugin(JmOptionPlugin):
388550
plugin_key = 'client_proxy'

0 commit comments

Comments
 (0)