Skip to content

Commit b6d69bd

Browse files
authored
EncryptedZipPlugin (#379)
1 parent 8331a16 commit b6d69bd

1 file changed

Lines changed: 172 additions & 0 deletions

File tree

src/jmcomic/jm_plugin.py

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

387559
class ClientProxyPlugin(JmOptionPlugin):
388560
plugin_key = 'client_proxy'

0 commit comments

Comments
 (0)