jmcomic 2.5.35__py3-none-any.whl → 2.5.37__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.35'
5
+ __version__ = '2.5.37'
6
6
 
7
7
  from .api import *
8
8
  from .jm_plugin import *
@@ -62,13 +62,16 @@ class JmImageResp(JmResp):
62
62
  img_url=None,
63
63
  ):
64
64
  img_url = img_url or self.url
65
+ index = img_url.find("?")
66
+ if index != -1:
67
+ img_url = img_url[0:index]
65
68
 
66
69
  if decode_image is False or scramble_id is None:
67
70
  # 不解密图片,直接保存文件
68
71
  JmImageTool.save_resp_img(
69
72
  self,
70
73
  path,
71
- need_convert=suffix_not_equal(img_url[:img_url.find("?")], path),
74
+ need_convert=suffix_not_equal(img_url, path),
72
75
  )
73
76
  else:
74
77
  # 解密图片并保存文件
jmcomic/jm_config.py CHANGED
@@ -414,7 +414,7 @@ class JmModuleConfig:
414
414
  'cache': None, # see CacheRegistry
415
415
  'domain': [],
416
416
  'postman': {
417
- 'type': 'cffi',
417
+ 'type': 'curl_cffi',
418
418
  'meta_data': {
419
419
  'impersonate': 'chrome110',
420
420
  'headers': None,
jmcomic/jm_entity.py CHANGED
@@ -164,6 +164,32 @@ class DetailEntity(JmBaseEntity, IndexedEntity):
164
164
 
165
165
  return getattr(detail, ref)
166
166
 
167
+ def get_properties_dict(self):
168
+ import inspect
169
+
170
+ prefix = self.__class__.__name__[2]
171
+ result = {}
172
+
173
+ # field
174
+ for k, v in self.__dict__.items():
175
+ result[prefix + k] = v
176
+
177
+ # property
178
+ for cls in inspect.getmro(type(self)):
179
+ for name, attr in cls.__dict__.items():
180
+ k = prefix + name
181
+ if k not in result and isinstance(attr, property):
182
+ v = attr.__get__(self, cls)
183
+ result[k] = v
184
+
185
+ # advice
186
+ advice_dict = JmModuleConfig.AFIELD_ADVICE if self.is_album() else JmModuleConfig.PFIELD_ADVICE
187
+ for name, func in advice_dict.items():
188
+ k = prefix + name
189
+ result[k] = func(self)
190
+
191
+ return result
192
+
167
193
 
168
194
  class JmImageDetail(JmBaseEntity, Downloadable):
169
195
 
jmcomic/jm_option.py CHANGED
@@ -58,90 +58,85 @@ class CacheRegistry:
58
58
 
59
59
 
60
60
  class DirRule:
61
- rule_sample = [
62
- # 根目录 / Album-id / Photo-序号 /
63
- 'Bd_Aid_Pindex', # 禁漫网站的默认下载方式
64
- # 根目录 / Album-作者 / Album-标题 / Photo-序号 /
65
- 'Bd_Aauthor_Atitle_Pindex',
66
- # 根目录 / Photo-序号&标题 /
67
- 'Bd_Pindextitle',
68
- # 根目录 / Photo-自定义类属性 /
69
- 'Bd_Aauthor_Atitle_Pcustomfield',
70
- # 需要替换JmModuleConfig.CLASS_ALBUM / CLASS_PHOTO才能让自定义属性生效
71
- ]
72
-
73
- Detail = Union[JmAlbumDetail, JmPhotoDetail, None]
74
- RuleFunc = Callable[[Detail], str]
75
- RuleSolver = Tuple[str, RuleFunc, str]
76
- RuleSolverList = List[RuleSolver]
61
+ RULE_BASE_DIR = 'Bd'
77
62
 
78
63
  def __init__(self, rule: str, base_dir=None):
79
64
  base_dir = JmcomicText.parse_to_abspath(base_dir)
80
65
  self.base_dir = base_dir
81
66
  self.rule_dsl = rule
82
- self.solver_list = self.get_role_solver_list(rule, base_dir)
67
+ self.parser_list: List[Tuple[str, Callable]] = self.get_rule_parser_list(rule)
83
68
 
84
69
  def decide_image_save_dir(self,
85
70
  album: JmAlbumDetail,
86
71
  photo: JmPhotoDetail,
87
72
  ) -> str:
88
- path_ls = []
89
- for solver in self.solver_list:
90
- try:
91
- ret = self.apply_rule_solver(album, photo, solver)
92
- except BaseException as e:
93
- # noinspection PyUnboundLocalVariable
94
- jm_log('dir_rule', f'路径规则"{solver[2]}"的解析出错: {e}, album={album}, photo={photo}')
95
- raise e
96
-
97
- path_ls.append(str(ret))
98
-
99
- return fix_filepath('/'.join(path_ls), is_dir=True)
73
+ return self._build_path_from_rules(album, photo)
100
74
 
101
75
  def decide_album_root_dir(self, album: JmAlbumDetail) -> str:
102
- path_ls = []
103
- for solver in self.solver_list:
104
- key, _, rule = solver
76
+ return self._build_path_from_rules(album, None, True)
105
77
 
106
- if key != 'Bd' and key != 'A':
78
+ def _build_path_from_rules(self, album, photo, only_album_rules=False) -> str:
79
+ path_ls = []
80
+ for rule, parser in self.parser_list:
81
+ if only_album_rules and not (rule == self.RULE_BASE_DIR or rule.startswith('A')):
107
82
  continue
108
83
 
109
84
  try:
110
- ret = self.apply_rule_solver(album, None, solver)
85
+ path = parser(album, photo, rule)
111
86
  except BaseException as e:
112
87
  # noinspection PyUnboundLocalVariable
113
- jm_log('dir_rule', f'路径规则"{rule}"的解析出错: {e}, album={album}')
88
+ jm_log('dir_rule', f'路径规则"{rule}"的解析出错: {e}, album={album}, photo={photo}')
114
89
  raise e
90
+ if parser != self.parse_bd_rule:
91
+ path = fix_windir_name(str(path)).strip()
115
92
 
116
- path_ls.append(str(ret))
93
+ path_ls.append(path)
117
94
 
118
95
  return fix_filepath('/'.join(path_ls), is_dir=True)
119
96
 
120
- def get_role_solver_list(self, rule_dsl: str, base_dir: str) -> RuleSolverList:
97
+ def get_rule_parser_list(self, rule_dsl: str):
121
98
  """
122
99
  解析下载路径dsl,得到一个路径规则解析列表
123
100
  """
124
101
 
125
102
  rule_list = self.split_rule_dsl(rule_dsl)
126
- solver_ls: List[DirRule.RuleSolver] = []
103
+ parser_list: list = []
127
104
 
128
105
  for rule in rule_list:
129
106
  rule = rule.strip()
130
- if rule == 'Bd':
131
- solver_ls.append(('Bd', lambda _: base_dir, 'Bd'))
107
+ if rule == self.RULE_BASE_DIR:
108
+ parser_list.append((rule, self.parse_bd_rule))
132
109
  continue
133
110
 
134
- rule_solver = self.get_rule_solver(rule)
135
- if rule_solver is None:
111
+ parser = self.get_rule_parser(rule)
112
+ if parser is None:
136
113
  ExceptionTool.raises(f'不支持的dsl: "{rule}" in "{rule_dsl}"')
137
114
 
138
- solver_ls.append(rule_solver)
115
+ parser_list.append((rule, parser))
116
+
117
+ return parser_list
118
+
119
+ # noinspection PyUnusedLocal
120
+ def parse_bd_rule(self, album, photo, rule):
121
+ return self.base_dir
122
+
123
+ @classmethod
124
+ def parse_f_string_rule(cls, album, photo, rule: str):
125
+ properties = {}
126
+ if album:
127
+ properties.update(album.get_properties_dict())
128
+ if photo:
129
+ properties.update(photo.get_properties_dict())
130
+ return rule.format(**properties)
139
131
 
140
- return solver_ls
132
+ @classmethod
133
+ def parse_detail_rule(cls, album, photo, rule: str):
134
+ detail = album if rule.startswith('A') else photo
135
+ return str(DetailEntity.get_dirname(detail, rule[1:]))
141
136
 
142
137
  # noinspection PyMethodMayBeStatic
143
- def split_rule_dsl(self, rule_dsl: str) -> List[str]:
144
- if rule_dsl == 'Bd':
138
+ def split_rule_dsl(self, rule_dsl: str):
139
+ if rule_dsl == self.RULE_BASE_DIR:
145
140
  return [rule_dsl]
146
141
 
147
142
  if '/' in rule_dsl:
@@ -153,42 +148,21 @@ class DirRule:
153
148
  ExceptionTool.raises(f'不支持的rule配置: "{rule_dsl}"')
154
149
 
155
150
  @classmethod
156
- def get_rule_solver(cls, rule: str) -> Optional[RuleSolver]:
157
- # 检查dsl
158
- if not rule.startswith(('A', 'P')):
159
- return None
160
-
161
- def solve_func(detail):
162
- return fix_windir_name(str(DetailEntity.get_dirname(detail, rule[1:]))).strip()
163
-
164
- return rule[0], solve_func, rule
165
-
166
- @classmethod
167
- def apply_rule_solver(cls, album, photo, rule_solver: RuleSolver) -> str:
168
- """
169
- 应用规则解析器(RuleSolver)
151
+ def get_rule_parser(cls, rule: str):
152
+ if '{' in rule:
153
+ return cls.parse_f_string_rule
170
154
 
171
- :param album: JmAlbumDetail
172
- :param photo: JmPhotoDetail
173
- :param rule_solver: Ptitle
174
- :returns: photo.title
175
- """
176
-
177
- def choose_detail(key):
178
- if key == 'Bd':
179
- return None
180
- if key == 'A':
181
- return album
182
- if key == 'P':
183
- return photo
155
+ if rule.startswith(('A', 'P')):
156
+ return cls.parse_detail_rule
184
157
 
185
- key, func, _ = rule_solver
186
- detail = choose_detail(key)
187
- return func(detail)
158
+ ExceptionTool.raises(f'不支持的rule配置: "{rule}"')
188
159
 
189
160
  @classmethod
190
161
  def apply_rule_directly(cls, album, photo, rule: str) -> str:
191
- return cls.apply_rule_solver(album, photo, cls.get_rule_solver(rule))
162
+ if album is None:
163
+ album = photo.from_album
164
+ # noinspection PyArgumentList
165
+ return fix_windir_name(cls.get_rule_parser(rule)(album, photo, rule)).strip()
192
166
 
193
167
 
194
168
  class JmOption:
@@ -463,7 +437,7 @@ class JmOption:
463
437
  orig_cookies.update(cookies)
464
438
  metadata['cookies'] = orig_cookies
465
439
 
466
- # noinspection PyMethodMayBeStatic
440
+ # noinspection PyMethodMayBeStatic,PyTypeChecker
467
441
  def decide_client_domain(self, client_key: str) -> List[str]:
468
442
  def is_client_type(ctype) -> bool:
469
443
  return self.client_key_is_given_type(client_key, ctype)
jmcomic/jm_plugin.py CHANGED
@@ -307,7 +307,7 @@ class ZipPlugin(JmOptionPlugin):
307
307
 
308
308
  elif level == 'photo':
309
309
  for photo, image_list in photo_dict.items():
310
- zip_path = self.get_zip_path(None, photo, filename_rule, suffix, zip_dir)
310
+ zip_path = self.get_zip_path(photo.from_album, photo, filename_rule, suffix, zip_dir)
311
311
  self.zip_photo(photo, image_list, zip_path, path_to_delete)
312
312
 
313
313
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jmcomic
3
- Version: 2.5.35
3
+ Version: 2.5.37
4
4
  Summary: Python API For JMComic (禁漫天堂)
5
5
  Home-page: https://github.com/hect0x7/JMComic-Crawler-Python
6
6
  Author: hect0x7
@@ -0,0 +1,18 @@
1
+ jmcomic/__init__.py,sha256=PoBTwaWIz9PGFi2pFAB2WlKz7bj_5r7ltJFoAZvmCu8,903
2
+ jmcomic/api.py,sha256=ZduhXDmh4lg1dkXHs7UTAaPpYNO7kcdPCDH5JJT9KSI,4253
3
+ jmcomic/cl.py,sha256=PBSh0JndNFZw3B7WJPj5Y8SeFdKzHE00jIwYo9An-K0,3475
4
+ jmcomic/jm_client_impl.py,sha256=m8-e_TA3vIXdUkXJcuJ0uzek4GM48y4QvBRXrT6JqUc,39633
5
+ jmcomic/jm_client_interface.py,sha256=Bld80cYwBwIqcMSJK09g5q8U_UX3IWnQJxuRJQwmIOk,19082
6
+ jmcomic/jm_config.py,sha256=p7g2FQMVXBnejz1sSzM15YAtJ5f91fZZeNMqFeYum5I,16772
7
+ jmcomic/jm_downloader.py,sha256=6w9D7eoL2M85-ScH745-OZBvaC0uwGkHi6awTUzq-3E,11036
8
+ jmcomic/jm_entity.py,sha256=x-3yUvM74C_Gof20mJw2SmZq7sSSsGNg0Cp47oZ-dFg,19968
9
+ jmcomic/jm_exception.py,sha256=x3KGMLlQS2zi1GX7z5G58zJN2EwLkI4mAURkxZYjEvA,5055
10
+ jmcomic/jm_option.py,sha256=6hwnm4UPnQPujsA3Gkt17krnJfUF435b-272hVLqlTk,21167
11
+ jmcomic/jm_plugin.py,sha256=1sSPbo2ieXW-asmXapJ0ZaB1Lx5co7SS5dfbYchJ1X4,39914
12
+ jmcomic/jm_toolkit.py,sha256=_qAJKbcV9jaFHRGvGU4Na2ts5h7QPGJS4uRQ05KpQ88,29166
13
+ jmcomic-2.5.37.dist-info/licenses/LICENSE,sha256=kz4coTxZxuGxisK3W00tjK57Zh3RcMGq-EnbXrK7-xA,1064
14
+ jmcomic-2.5.37.dist-info/METADATA,sha256=hovvk8BXYGUUPaMUnqRPIQWPyFEeOkuPnGYkg3nfsMY,8161
15
+ jmcomic-2.5.37.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
16
+ jmcomic-2.5.37.dist-info/entry_points.txt,sha256=tRbQltaGSBjejI0c9jYt-4SXQMd5nSDHcMvHmuTy4ow,44
17
+ jmcomic-2.5.37.dist-info/top_level.txt,sha256=puvVMFYJqIbd6NOTMEvOyugMTT8woBfSQyxEBan3zY4,8
18
+ jmcomic-2.5.37.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (79.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,18 +0,0 @@
1
- jmcomic/__init__.py,sha256=5Qyc6r1XhjnQJsvyr2bhgqDvxd0lAr-6U4SeOzAlyAs,903
2
- jmcomic/api.py,sha256=ZduhXDmh4lg1dkXHs7UTAaPpYNO7kcdPCDH5JJT9KSI,4253
3
- jmcomic/cl.py,sha256=PBSh0JndNFZw3B7WJPj5Y8SeFdKzHE00jIwYo9An-K0,3475
4
- jmcomic/jm_client_impl.py,sha256=m8-e_TA3vIXdUkXJcuJ0uzek4GM48y4QvBRXrT6JqUc,39633
5
- jmcomic/jm_client_interface.py,sha256=LqJqa-4G7-F98_U9R9QTxFND_9t30B-BIQZa_o0p-KY,19005
6
- jmcomic/jm_config.py,sha256=rOeKMqZ3HwWk3I-vA0YTCIimBcfdZpvXj7Itg0OKfCo,16767
7
- jmcomic/jm_downloader.py,sha256=6w9D7eoL2M85-ScH745-OZBvaC0uwGkHi6awTUzq-3E,11036
8
- jmcomic/jm_entity.py,sha256=vz_nZbEtRLBRrVz97533Twf9mxB1AlwauunCvdhFa7w,19190
9
- jmcomic/jm_exception.py,sha256=x3KGMLlQS2zi1GX7z5G58zJN2EwLkI4mAURkxZYjEvA,5055
10
- jmcomic/jm_option.py,sha256=2tINFsDcncCJbd6FRtCMAidhro6faB8TBLoaSxP3uQ8,21926
11
- jmcomic/jm_plugin.py,sha256=e2B_x2adghxn7f8eKAJivDWf8oLY9962Utt1-J70zBw,39902
12
- jmcomic/jm_toolkit.py,sha256=_qAJKbcV9jaFHRGvGU4Na2ts5h7QPGJS4uRQ05KpQ88,29166
13
- jmcomic-2.5.35.dist-info/licenses/LICENSE,sha256=kz4coTxZxuGxisK3W00tjK57Zh3RcMGq-EnbXrK7-xA,1064
14
- jmcomic-2.5.35.dist-info/METADATA,sha256=urkZ5ZX0yGsUOhLpc8yY-LGwwMZPAu9rcQTN8cAwbtc,8161
15
- jmcomic-2.5.35.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
16
- jmcomic-2.5.35.dist-info/entry_points.txt,sha256=tRbQltaGSBjejI0c9jYt-4SXQMd5nSDHcMvHmuTy4ow,44
17
- jmcomic-2.5.35.dist-info/top_level.txt,sha256=puvVMFYJqIbd6NOTMEvOyugMTT8woBfSQyxEBan3zY4,8
18
- jmcomic-2.5.35.dist-info/RECORD,,