jmcomic 2.5.11__py3-none-any.whl → 2.5.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
jmcomic/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # 被依赖方 <--- 使用方
3
3
  # config <--- entity <--- toolkit <--- client <--- option <--- downloader
4
4
 
5
- __version__ = '2.5.11'
5
+ __version__ = '2.5.12'
6
6
 
7
7
  from .api import *
8
8
  from .jm_plugin import *
@@ -581,6 +581,6 @@ class JmcomicClient(
581
581
  """
582
582
  if isinstance(self, ctype):
583
583
  return True
584
- if self.client_key == instance.client_key:
584
+ if self.client_key == ctype.client_key:
585
585
  return True
586
586
  return False
jmcomic/jm_config.py CHANGED
@@ -109,14 +109,17 @@ class JmModuleConfig:
109
109
  DOMAIN_IMAGE_LIST = str_to_list('''
110
110
  cdn-msp.jmapinodeudzn.net
111
111
  cdn-msp2.jmapinodeudzn.net
112
+ cdn-msp2.jmapiproxy3.cc
113
+ cdn-msp3.jmapinodeudzn.net
112
114
 
113
115
  ''')
114
116
 
115
117
  # 移动端API域名
116
118
  DOMAIN_API_LIST = str_to_list('''
117
119
  www.jmapinodeudzn.xyz
118
- www.jmapinode.vip
119
- www.jmapinode.biz
120
+ www.cdn-eldenringproxy.xyz
121
+ www.cdn-eldenringproxy.me
122
+ www.cdn-eldenringproxy.vip
120
123
  www.jmapinode.xyz
121
124
  ''')
122
125
 
jmcomic/jm_option.py CHANGED
@@ -72,11 +72,9 @@ class DirRule:
72
72
 
73
73
  Detail = Union[JmAlbumDetail, JmPhotoDetail, None]
74
74
  RuleFunc = Callable[[Detail], str]
75
- RuleSolver = Tuple[int, RuleFunc, str]
75
+ RuleSolver = Tuple[str, RuleFunc, str]
76
76
  RuleSolverList = List[RuleSolver]
77
77
 
78
- rule_solver_cache: Dict[str, RuleSolver] = {}
79
-
80
78
  def __init__(self, rule: str, base_dir=None):
81
79
  base_dir = JmcomicText.parse_to_abspath(base_dir)
82
80
  self.base_dir = base_dir
@@ -100,6 +98,25 @@ class DirRule:
100
98
 
101
99
  return fix_filepath('/'.join(path_ls), is_dir=True)
102
100
 
101
+ def decide_album_root_dir(self, album: JmAlbumDetail) -> str:
102
+ path_ls = []
103
+ for solver in self.solver_list:
104
+ key, _, rule = solver
105
+
106
+ if key != 'Bd' and key != 'A':
107
+ continue
108
+
109
+ try:
110
+ ret = self.apply_rule_solver(album, None, solver)
111
+ except BaseException as e:
112
+ # noinspection PyUnboundLocalVariable
113
+ jm_log('dir_rule', f'路径规则"{rule}"的解析出错: {e}, album={album}')
114
+ raise e
115
+
116
+ path_ls.append(str(ret))
117
+
118
+ return fix_filepath('/'.join(path_ls), is_dir=True)
119
+
103
120
  def get_role_solver_list(self, rule_dsl: str, base_dir: str) -> RuleSolverList:
104
121
  """
105
122
  解析下载路径dsl,得到一个路径规则解析列表
@@ -111,7 +128,7 @@ class DirRule:
111
128
  for rule in rule_list:
112
129
  rule = rule.strip()
113
130
  if rule == 'Bd':
114
- solver_ls.append((0, lambda _: base_dir, 'Bd'))
131
+ solver_ls.append(('Bd', lambda _: base_dir, 'Bd'))
115
132
  continue
116
133
 
117
134
  rule_solver = self.get_rule_solver(rule)
@@ -137,24 +154,14 @@ class DirRule:
137
154
 
138
155
  @classmethod
139
156
  def get_rule_solver(cls, rule: str) -> Optional[RuleSolver]:
140
- # 查找缓存
141
- if rule in cls.rule_solver_cache:
142
- return cls.rule_solver_cache[rule]
143
-
144
157
  # 检查dsl
145
158
  if not rule.startswith(('A', 'P')):
146
159
  return None
147
160
 
148
- # Axxx or Pyyy
149
- key = 1 if rule[0] == 'A' else 2
150
-
151
161
  def solve_func(detail):
152
162
  return fix_windir_name(str(DetailEntity.get_dirname(detail, rule[1:])))
153
163
 
154
- # 保存缓存
155
- rule_solver = (key, solve_func, rule)
156
- cls.rule_solver_cache[rule] = rule_solver
157
- return rule_solver
164
+ return rule[0], solve_func, rule
158
165
 
159
166
  @classmethod
160
167
  def apply_rule_solver(cls, album, photo, rule_solver: RuleSolver) -> str:
@@ -168,11 +175,11 @@ class DirRule:
168
175
  """
169
176
 
170
177
  def choose_detail(key):
171
- if key == 0:
178
+ if key == 'Bd':
172
179
  return None
173
- if key == 1:
180
+ if key == 'A':
174
181
  return album
175
- if key == 2:
182
+ if key == 'P':
176
183
  return photo
177
184
 
178
185
  key, func, _ = rule_solver
jmcomic/jm_plugin.py CHANGED
@@ -1035,3 +1035,62 @@ class SkipPhotoWithFewImagesPlugin(JmOptionPlugin):
1035
1035
  @field_cache() # 单例
1036
1036
  def build(cls, option: JmOption) -> 'JmOptionPlugin':
1037
1037
  return super().build(option)
1038
+
1039
+
1040
+ class DeleteDuplicatedFilesPlugin(JmOptionPlugin):
1041
+ """
1042
+ https://github.com/hect0x7/JMComic-Crawler-Python/issues/244
1043
+ """
1044
+ plugin_key = 'delete_duplicated_files'
1045
+
1046
+ @classmethod
1047
+ def calculate_md5(cls, file_path):
1048
+ import hashlib
1049
+
1050
+ """计算文件的MD5哈希值"""
1051
+ hash_md5 = hashlib.md5()
1052
+ with open(file_path, "rb") as f:
1053
+ for chunk in iter(lambda: f.read(4096), b""):
1054
+ hash_md5.update(chunk)
1055
+ return hash_md5.hexdigest()
1056
+
1057
+ @classmethod
1058
+ def find_duplicate_files(cls, root_folder):
1059
+ """递归读取文件夹下所有文件并计算MD5出现次数"""
1060
+ import os
1061
+ from collections import defaultdict
1062
+ md5_dict = defaultdict(list)
1063
+
1064
+ for root, _, files in os.walk(root_folder):
1065
+ for file in files:
1066
+ file_path = os.path.join(root, file)
1067
+ file_md5 = cls.calculate_md5(file_path)
1068
+ md5_dict[file_md5].append(file_path)
1069
+
1070
+ return md5_dict
1071
+
1072
+ def invoke(self,
1073
+ limit,
1074
+ album=None,
1075
+ downloader=None,
1076
+ delete_original_file=True,
1077
+ **kwargs,
1078
+ ) -> None:
1079
+ if album is None:
1080
+ return
1081
+
1082
+ self.delete_original_file = delete_original_file
1083
+ # 获取到下载本子所在根目录
1084
+ root_folder = self.option.dir_rule.decide_album_root_dir(album)
1085
+ self.find_duplicated_files_and_delete(limit, root_folder, album)
1086
+
1087
+ def find_duplicated_files_and_delete(self, limit: int, root_folder: str, album: Optional[JmAlbumDetail] = None):
1088
+ md5_dict = self.find_duplicate_files(root_folder)
1089
+ # 打印MD5出现次数大于等于limit的文件
1090
+ for md5, paths in md5_dict.items():
1091
+ if len(paths) >= limit:
1092
+ prefix = '' if album is None else f'({album.album_id}) '
1093
+ message = [prefix + f'MD5: {md5} 出现次数: {len(paths)}'] + \
1094
+ [f' {path}' for path in paths]
1095
+ self.log('\n'.join(message))
1096
+ self.execute_deletion(paths)
jmcomic/jm_toolkit.py CHANGED
@@ -707,7 +707,7 @@ class JmImageTool:
707
707
  如果需要改变图片的文件格式,比如 .jpg → .png,则需要指定参数 neet_convert=True.
708
708
  如果不需要改变图片的文件格式,使用 need_convert=False,可以跳过PIL解析图片,效率更高.
709
709
 
710
- :param resp: HTTP响应对象
710
+ :param resp: JmImageResp
711
711
  :param filepath: 图片文件路径
712
712
  :param need_convert: 是否转换图片
713
713
  """
@@ -746,7 +746,7 @@ class JmImageTool:
746
746
 
747
747
  # 无需解密,直接保存
748
748
  if num == 0:
749
- img_src.save(decoded_save_path)
749
+ cls.save_image(img_src, decoded_save_path)
750
750
  return
751
751
 
752
752
  import math
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jmcomic
3
- Version: 2.5.11
3
+ Version: 2.5.12
4
4
  Summary: Python API For JMComic (禁漫天堂)
5
5
  Home-page: https://github.com/hect0x7/JMComic-Crawler-Python
6
6
  Author: hect0x7
@@ -71,27 +71,50 @@ Requires-Dist: pycryptodome
71
71
 
72
72
  ## 快速上手
73
73
 
74
- 使用下面的两行代码,即可实现功能:把某个本子(album)里的所有章节(photo)下载到本地
75
-
74
+ ### 1. 下载本子方法
75
+ 只需要使用如下代码,就可以下载本子`JM422866`的所有章节的图片:
76
76
  ```python
77
77
  import jmcomic # 导入此模块,需要先安装.
78
78
  jmcomic.download_album('422866') # 传入要下载的album的id,即可下载整个album到本地.
79
79
  ```
80
80
 
81
- * v2.2.9: 新增命令行调用方式,上述的代码可以转为一行命令
81
+ 上面的 `download_album`方法还有一个参数`option`,可用于控制下载配置,配置包括禁漫域名、网络代理、图片格式转换、插件等等。
82
+
83
+ 你可能需要这些配置项。推荐使用配置文件创建option,用option下载本子,见下章:
84
+
85
+ ### 2. 使用option配置来下载本子
86
+
87
+ 1. 首先,创建一个配置文件,假设文件名为 `option.yml`
88
+
89
+ 该文件有特定的写法,你需要参考这个文档 → [option配置](./assets/docs/sources/option_file_syntax.md)
90
+
91
+ 下面做一个演示,假设你需要把下载的图片转为png格式,你应该把以下内容写进`option.yml`
92
+
93
+ ```yml
94
+ download:
95
+ image:
96
+ suffix: .png # 该配置用于把下载的图片转为png格式
97
+ ```
98
+
99
+ 2. 第二步,运行下面的python代码
100
+ ```python
101
+ import jmcomic
82
102
 
83
- ```bash
84
- # 下载album_id为422866的本子
85
- $ jmcomic 422866
103
+ # 创建配置对象
104
+ option = jmcomic.create_option_by_file('你的配置文件路径,例如 D:/option.yml')
105
+ # 使用option对象来下载本子
106
+ jmcomic.download_album(422866, option)
86
107
  ```
87
108
 
109
+
110
+
88
111
  ## 进阶使用
89
112
 
90
113
  文档网站:[jmcomic.readthedocs.io](https://jmcomic.readthedocs.io/en/latest)
91
114
 
92
- 进阶使用可以参考:[jmcomic常用类和方法演示](assets/docs/sources/tutorial/0_demo.md)
115
+ 首先,就下载功能,jmcomic提供了很多配置项,大部分的下载需求你都可以通过上文介绍的配置文件来配置。
93
116
 
94
- 下面列出的是一些常用的文档:
117
+ 如果你不满足于下载,还有其他的使用需求,推荐你先看看以下文档:
95
118
 
96
119
  * [jmcomic常用类和方法演示](assets/docs/sources/tutorial/0_demo.md)
97
120
  * [option配置文件语法(包含插件配置)](./assets/docs/sources/option_file_syntax.md)
@@ -0,0 +1,18 @@
1
+ jmcomic/__init__.py,sha256=1QCB9drWLz_G92PcVXTxmj-hCjpl4qW4NKNwvTxc0pA,903
2
+ jmcomic/api.py,sha256=uLHtSof7ZaiWZcNDDAz6grD1NYs4VxsoCOrsge3G7v8,3864
3
+ jmcomic/cl.py,sha256=PBSh0JndNFZw3B7WJPj5Y8SeFdKzHE00jIwYo9An-K0,3475
4
+ jmcomic/jm_client_impl.py,sha256=Oszkw7dKfg8DHzmjyBVD1DrHp9OlvLK33IQHB7uXo48,37811
5
+ jmcomic/jm_client_interface.py,sha256=UmwIZt-7-zHfBHq8sNFnfNf-C5kkCQW8_yKKI4cfJYU,17688
6
+ jmcomic/jm_config.py,sha256=z512nMjxJldxRbn8Kt02DrBZ89wLF3cR5_OtY1pemuA,15359
7
+ jmcomic/jm_downloader.py,sha256=0r4z7FRnow6xkRy_WTv7nLQOhYdtZmoouw0BNrhngco,10397
8
+ jmcomic/jm_entity.py,sha256=-WUKYGkNToQv5Hja3RkI5abHF1g46WpLyVjoyXMs-wI,18974
9
+ jmcomic/jm_exception.py,sha256=B9APE1jw23JBzCT12eV_imCZny3mNrSXju1p2IquaHA,4801
10
+ jmcomic/jm_option.py,sha256=4J7RJi1D2plVjNhPXE2QSjMuBeQzlpeAwRCojWqNqnw,22025
11
+ jmcomic/jm_plugin.py,sha256=F5mvqDNU0ir4XUNWpYroV4EdVR3bDBB-FOS15_l1T4k,35732
12
+ jmcomic/jm_toolkit.py,sha256=Skd7bp008Brr3hiXUlm3lgGpPsB1UmmX9XU2Dbx3jps,28655
13
+ jmcomic-2.5.12.dist-info/LICENSE,sha256=kz4coTxZxuGxisK3W00tjK57Zh3RcMGq-EnbXrK7-xA,1064
14
+ jmcomic-2.5.12.dist-info/METADATA,sha256=8A6vYjiV74XxGq-B0NKwN8U4N7104yye6Ri8kSGt-HM,7075
15
+ jmcomic-2.5.12.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
16
+ jmcomic-2.5.12.dist-info/entry_points.txt,sha256=tRbQltaGSBjejI0c9jYt-4SXQMd5nSDHcMvHmuTy4ow,44
17
+ jmcomic-2.5.12.dist-info/top_level.txt,sha256=puvVMFYJqIbd6NOTMEvOyugMTT8woBfSQyxEBan3zY4,8
18
+ jmcomic-2.5.12.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- jmcomic/__init__.py,sha256=Hjp1Q6tJ5zCFkSqh2ETXJuh_Rgpx5OKEjMhSbrU3dTs,903
2
- jmcomic/api.py,sha256=uLHtSof7ZaiWZcNDDAz6grD1NYs4VxsoCOrsge3G7v8,3864
3
- jmcomic/cl.py,sha256=PBSh0JndNFZw3B7WJPj5Y8SeFdKzHE00jIwYo9An-K0,3475
4
- jmcomic/jm_client_impl.py,sha256=Oszkw7dKfg8DHzmjyBVD1DrHp9OlvLK33IQHB7uXo48,37811
5
- jmcomic/jm_client_interface.py,sha256=vP36T2OI5MQdmfdYEnoFGRpHZRTE9LnBetDUFCOnMg4,17691
6
- jmcomic/jm_config.py,sha256=I08aDW1WH9uVnbyiUvy4lfI8cAU8NNK96cEA5kGYcoM,15252
7
- jmcomic/jm_downloader.py,sha256=0r4z7FRnow6xkRy_WTv7nLQOhYdtZmoouw0BNrhngco,10397
8
- jmcomic/jm_entity.py,sha256=-WUKYGkNToQv5Hja3RkI5abHF1g46WpLyVjoyXMs-wI,18974
9
- jmcomic/jm_exception.py,sha256=B9APE1jw23JBzCT12eV_imCZny3mNrSXju1p2IquaHA,4801
10
- jmcomic/jm_option.py,sha256=s16w8uZYfCBUgsxCjegnLoAqHF69rJicVZKQTZlQTJA,21723
11
- jmcomic/jm_plugin.py,sha256=21bd71tmsKyli73skMx0ozcqBM5saHzICEicjRQ98-Q,33606
12
- jmcomic/jm_toolkit.py,sha256=WLHaigCN03Z4b5swGTRTTSWzZsa0uFxGAnw4xKhbh1U,28649
13
- jmcomic-2.5.11.dist-info/LICENSE,sha256=kz4coTxZxuGxisK3W00tjK57Zh3RcMGq-EnbXrK7-xA,1064
14
- jmcomic-2.5.11.dist-info/METADATA,sha256=2vGTgKwdQwztHQ8eIQ9cMOdHb5FTKYCPRZ_uWP6qP0E,6123
15
- jmcomic-2.5.11.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
16
- jmcomic-2.5.11.dist-info/entry_points.txt,sha256=tRbQltaGSBjejI0c9jYt-4SXQMd5nSDHcMvHmuTy4ow,44
17
- jmcomic-2.5.11.dist-info/top_level.txt,sha256=puvVMFYJqIbd6NOTMEvOyugMTT8woBfSQyxEBan3zY4,8
18
- jmcomic-2.5.11.dist-info/RECORD,,