jmcomic 2.5.7__py3-none-any.whl → 2.5.9__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.7'
5
+ __version__ = '2.5.9'
6
6
 
7
7
  from .api import *
8
8
  from .jm_plugin import *
jmcomic/jm_client_impl.py CHANGED
@@ -224,7 +224,7 @@ class AbstractJmClient(
224
224
 
225
225
  # noinspection PyMethodMayBeStatic
226
226
  def decode(self, url: str):
227
- if not JmModuleConfig.flag_decode_url_when_logging or '/search/' not in url:
227
+ if not JmModuleConfig.FLAG_DECODE_URL_WHEN_LOGGING or '/search/' not in url:
228
228
  return url
229
229
 
230
230
  from urllib.parse import unquote
@@ -767,7 +767,7 @@ class JmApiClient(AbstractJmClient):
767
767
  # 检查禁漫最新的版本号
768
768
  setting_ver = str(resp.model_data.version)
769
769
  # 禁漫接口的版本 > jmcomic库内置版本
770
- if setting_ver > JmMagicConstants.APP_VERSION and JmModuleConfig.flag_use_version_newer_if_behind:
770
+ if setting_ver > JmMagicConstants.APP_VERSION and JmModuleConfig.FLAG_USE_VERSION_NEWER_IF_BEHIND:
771
771
  jm_log('api.setting', f'change APP_VERSION from [{JmMagicConstants.APP_VERSION}] to [{setting_ver}]')
772
772
  JmMagicConstants.APP_VERSION = setting_ver
773
773
 
@@ -883,7 +883,7 @@ class JmApiClient(AbstractJmClient):
883
883
  ts = time_stamp()
884
884
  token, tokenparam = JmCryptoTool.token_and_tokenparam(ts, secret=JmMagicConstants.APP_TOKEN_SECRET_2)
885
885
 
886
- elif JmModuleConfig.flag_use_fix_timestamp:
886
+ elif JmModuleConfig.FLAG_USE_FIX_TIMESTAMP:
887
887
  ts, token, tokenparam = JmModuleConfig.get_fix_ts_token_tokenparam()
888
888
 
889
889
  else:
@@ -954,7 +954,7 @@ class JmApiClient(AbstractJmClient):
954
954
 
955
955
  def after_init(self):
956
956
  # 保证拥有cookies,因为移动端要求必须携带cookies,否则会直接跳转同一本子【禁漫娘】
957
- if JmModuleConfig.flag_api_client_require_cookies:
957
+ if JmModuleConfig.FLAG_API_CLIENT_REQUIRE_COOKIES:
958
958
  self.ensure_have_cookies()
959
959
 
960
960
  client_init_cookies_lock = Lock()
@@ -88,8 +88,8 @@ class JmJsonResp(JmResp):
88
88
  except Exception as e:
89
89
  ExceptionTool.raises_resp(f'json解析失败: {e}', self, JsonResolveFailException)
90
90
 
91
- def model(self) -> AdvancedEasyAccessDict:
92
- return AdvancedEasyAccessDict(self.json())
91
+ def model(self) -> AdvancedDict:
92
+ return AdvancedDict(self.json())
93
93
 
94
94
 
95
95
  class JmApiResp(JmJsonResp):
@@ -118,9 +118,9 @@ class JmApiResp(JmJsonResp):
118
118
  return loads(self.decoded_data)
119
119
 
120
120
  @property
121
- def model_data(self) -> AdvancedEasyAccessDict:
121
+ def model_data(self) -> AdvancedDict:
122
122
  self.require_success()
123
- return AdvancedEasyAccessDict(self.res_data)
123
+ return AdvancedDict(self.res_data)
124
124
 
125
125
 
126
126
  # album-comment
jmcomic/jm_config.py CHANGED
@@ -146,18 +146,18 @@ class JmModuleConfig:
146
146
  REGISTRY_EXCEPTION_LISTENER = {}
147
147
 
148
148
  # 执行log的函数
149
- executor_log = default_jm_logging
149
+ EXECUTOR_LOG = default_jm_logging
150
150
 
151
151
  # 使用固定时间戳
152
- flag_use_fix_timestamp = True
152
+ FLAG_USE_FIX_TIMESTAMP = True
153
153
  # 移动端Client初始化cookies
154
- flag_api_client_require_cookies = True
154
+ FLAG_API_CLIENT_REQUIRE_COOKIES = True
155
155
  # log开关标记
156
- flag_enable_jm_log = True
156
+ FLAG_ENABLE_JM_LOG = True
157
157
  # log时解码url
158
- flag_decode_url_when_logging = True
158
+ FLAG_DECODE_URL_WHEN_LOGGING = True
159
159
  # 当内置的版本号落后时,使用最新的禁漫app版本号
160
- flag_use_version_newer_if_behind = True
160
+ FLAG_USE_VERSION_NEWER_IF_BEHIND = True
161
161
 
162
162
  # 关联dir_rule的自定义字段与对应的处理函数
163
163
  # 例如:
@@ -165,6 +165,10 @@ class JmModuleConfig:
165
165
  AFIELD_ADVICE = dict()
166
166
  PFIELD_ADVICE = dict()
167
167
 
168
+ # 当发生 oserror: [Errno 36] File name too long 时,
169
+ # 把文件名限制在指定个字符以内
170
+ VAR_FILE_NAME_LENGTH_LIMIT = 100
171
+
168
172
  @classmethod
169
173
  def downloader_class(cls):
170
174
  if cls.CLASS_DOWNLOADER is not None:
@@ -319,12 +323,12 @@ class JmModuleConfig:
319
323
  # noinspection PyUnusedLocal
320
324
  @classmethod
321
325
  def jm_log(cls, topic: str, msg: str):
322
- if cls.flag_enable_jm_log is True:
323
- cls.executor_log(topic, msg)
326
+ if cls.FLAG_ENABLE_JM_LOG is True:
327
+ cls.EXECUTOR_LOG(topic, msg)
324
328
 
325
329
  @classmethod
326
330
  def disable_jm_log(cls):
327
- cls.flag_enable_jm_log = False
331
+ cls.FLAG_ENABLE_JM_LOG = False
328
332
 
329
333
  @classmethod
330
334
  def new_postman(cls, session=False, **kwargs):
@@ -347,7 +351,7 @@ class JmModuleConfig:
347
351
  DEFAULT_CLIENT_CACHE = None # 默认关闭Client缓存。缓存的配置详见 CacheRegistry
348
352
  DEFAULT_PROXIES = ProxyBuilder.system_proxy() # 默认使用系统代理
349
353
 
350
- default_option_dict: dict = {
354
+ DEFAULT_OPTION_DICT: dict = {
351
355
  'log': None,
352
356
  'dir_rule': {'rule': 'Bd_Pname', 'base_dir': None},
353
357
  'download': {
@@ -364,7 +368,7 @@ class JmModuleConfig:
364
368
  'postman': {
365
369
  'type': 'cffi',
366
370
  'meta_data': {
367
- 'impersonate': 'chrome110',
371
+ 'impersonate': 'chrome',
368
372
  'headers': None,
369
373
  'proxies': None,
370
374
  }
@@ -387,11 +391,11 @@ class JmModuleConfig:
387
391
  """
388
392
  from copy import deepcopy
389
393
 
390
- option_dict = deepcopy(cls.default_option_dict)
394
+ option_dict = deepcopy(cls.DEFAULT_OPTION_DICT)
391
395
 
392
396
  # log
393
397
  if option_dict['log'] is None:
394
- option_dict['log'] = cls.flag_enable_jm_log
398
+ option_dict['log'] = cls.FLAG_ENABLE_JM_LOG
395
399
 
396
400
  # dir_rule.base_dir
397
401
  dir_rule = option_dict['dir_rule']
jmcomic/jm_downloader.py CHANGED
@@ -29,7 +29,7 @@ class DownloadCallback:
29
29
  f'章节下载完成: [{photo.id}] ({photo.album_id}[{photo.index}/{len(photo.from_album)}])')
30
30
 
31
31
  def before_image(self, image: JmImageDetail, img_save_path):
32
- if image.is_exists:
32
+ if image.exists:
33
33
  jm_log('image.before',
34
34
  f'图片已存在: {image.tag} ← [{img_save_path}]'
35
35
  )
@@ -63,6 +63,8 @@ class JmDownloader(DownloadCallback):
63
63
 
64
64
  def download_by_album_detail(self, album: JmAlbumDetail, client: JmcomicClient):
65
65
  self.before_album(album)
66
+ if album.skip:
67
+ return
66
68
  self.execute_by_condition(
67
69
  iter_objs=album,
68
70
  apply=lambda photo: self.download_by_photo_detail(photo, client),
@@ -80,6 +82,8 @@ class JmDownloader(DownloadCallback):
80
82
  client.check_photo(photo)
81
83
 
82
84
  self.before_photo(photo)
85
+ if photo.skip:
86
+ return
83
87
  self.execute_by_condition(
84
88
  iter_objs=photo,
85
89
  apply=lambda image: self.download_by_image_detail(image, client),
@@ -91,16 +95,19 @@ class JmDownloader(DownloadCallback):
91
95
  img_save_path = self.option.decide_image_filepath(image)
92
96
 
93
97
  image.save_path = img_save_path
94
- image.is_exists = file_exists(img_save_path)
98
+ image.exists = file_exists(img_save_path)
95
99
 
96
100
  self.before_image(image, img_save_path)
97
101
 
102
+ if image.skip:
103
+ return
104
+
98
105
  # let option decide use_cache and decode_image
99
106
  use_cache = self.option.decide_download_cache(image)
100
107
  decode_image = self.option.decide_download_image_decode(image)
101
108
 
102
109
  # skip download
103
- if use_cache is True and image.is_exists:
110
+ if use_cache is True and image.exists:
104
111
  return
105
112
 
106
113
  e = None
jmcomic/jm_entity.py CHANGED
@@ -3,6 +3,14 @@ from common import *
3
3
  from .jm_config import *
4
4
 
5
5
 
6
+ class Downloadable:
7
+
8
+ def __init__(self):
9
+ self.save_path: str = ''
10
+ self.exists: bool = False
11
+ self.skip = False
12
+
13
+
6
14
  class JmBaseEntity:
7
15
 
8
16
  def to_file(self, filepath):
@@ -117,7 +125,7 @@ class DetailEntity(JmBaseEntity, IndexedEntity):
117
125
  def __str__(self):
118
126
  return f'{self.__class__.__name__}' \
119
127
  '{' \
120
- f'{self.id}: {self.title}'\
128
+ f'{self.id}: {self.title}' \
121
129
  '}'
122
130
 
123
131
  @classmethod
@@ -156,7 +164,7 @@ class DetailEntity(JmBaseEntity, IndexedEntity):
156
164
  return getattr(detail, ref)
157
165
 
158
166
 
159
- class JmImageDetail(JmBaseEntity):
167
+ class JmImageDetail(JmBaseEntity, Downloadable):
160
168
 
161
169
  def __init__(self,
162
170
  aid,
@@ -167,7 +175,8 @@ class JmImageDetail(JmBaseEntity):
167
175
  from_photo=None,
168
176
  query_params=None,
169
177
  index=-1,
170
- ) -> None:
178
+ ):
179
+ super().__init__()
171
180
  if scramble_id is None or (isinstance(scramble_id, str) and scramble_id == ''):
172
181
  from .jm_toolkit import ExceptionTool
173
182
  ExceptionTool.raises(f'图片的scramble_id不能为空')
@@ -182,10 +191,6 @@ class JmImageDetail(JmBaseEntity):
182
191
  self.query_params: Optional[str] = query_params
183
192
  self.index = index # 从1开始
184
193
 
185
- # temp fields, in order to simplify passing parameter
186
- self.save_path: str = ''
187
- self.is_exists: bool = False
188
-
189
194
  @property
190
195
  def filename_without_suffix(self):
191
196
  return self.img_file_name
@@ -252,7 +257,7 @@ class JmImageDetail(JmBaseEntity):
252
257
  return True
253
258
 
254
259
 
255
- class JmPhotoDetail(DetailEntity):
260
+ class JmPhotoDetail(DetailEntity, Downloadable):
256
261
 
257
262
  def __init__(self,
258
263
  photo_id,
@@ -267,6 +272,7 @@ class JmPhotoDetail(DetailEntity):
267
272
  author=None,
268
273
  from_album=None,
269
274
  ):
275
+ super().__init__()
270
276
  self.photo_id: str = str(photo_id)
271
277
  self.scramble_id: str = str(scramble_id)
272
278
  self.name: str = str(name).strip()
@@ -411,7 +417,7 @@ class JmPhotoDetail(DetailEntity):
411
417
  return True
412
418
 
413
419
 
414
- class JmAlbumDetail(DetailEntity):
420
+ class JmAlbumDetail(DetailEntity, Downloadable):
415
421
 
416
422
  def __init__(self,
417
423
  album_id,
@@ -430,6 +436,7 @@ class JmAlbumDetail(DetailEntity):
430
436
  tags,
431
437
  related_list=None,
432
438
  ):
439
+ super().__init__()
433
440
  self.album_id: str = str(album_id)
434
441
  self.scramble_id: str = str(scramble_id)
435
442
  self.name: str = name
jmcomic/jm_exception.py CHANGED
@@ -12,6 +12,8 @@ class JmcomicException(Exception):
12
12
  def from_context(self, key):
13
13
  return self.context[key]
14
14
 
15
+ def __str__(self):
16
+ return self.msg
15
17
 
16
18
  class ResponseUnexpectedException(JmcomicException):
17
19
  description = '响应不符合预期异常'
@@ -70,13 +72,6 @@ class ExceptionTool:
70
72
  CONTEXT_KEY_RE_PATTERN = 'pattern'
71
73
  CONTEXT_KEY_MISSING_JM_ID = 'missing_jm_id'
72
74
 
73
- # 兼容旧版本
74
-
75
- EXTRA_KEY_RESP = 'resp'
76
- EXTRA_KEY_HTML = 'html'
77
- EXTRA_KEY_RE_PATTERN = 'pattern'
78
- EXTRA_KEY_MISSING_JM_ID = 'missing_jm_id'
79
-
80
75
  @classmethod
81
76
  def raises(cls,
82
77
  msg: str,
@@ -142,7 +137,8 @@ class ExceptionTool:
142
137
  :param resp: 响应对象
143
138
  :param jmid: 禁漫本子/章节id
144
139
  """
145
- url = resp.url
140
+ from .jm_toolkit import JmcomicText
141
+ url = JmcomicText.format_album_url(jmid)
146
142
 
147
143
  req_type = "本子" if "album" in url else "章节"
148
144
  cls.raises(
jmcomic/jm_option.py CHANGED
@@ -197,11 +197,11 @@ class JmOption:
197
197
  # 路径规则配置
198
198
  self.dir_rule = DirRule(**dir_rule)
199
199
  # 客户端配置
200
- self.client = AdvancedEasyAccessDict(client)
200
+ self.client = AdvancedDict(client)
201
201
  # 下载配置
202
- self.download = AdvancedEasyAccessDict(download)
202
+ self.download = AdvancedDict(download)
203
203
  # 插件配置
204
- self.plugins = AdvancedEasyAccessDict(plugins)
204
+ self.plugins = AdvancedDict(plugins)
205
205
  # 其他配置
206
206
  self.filepath = filepath
207
207
 
@@ -285,7 +285,15 @@ class JmOption:
285
285
  )
286
286
 
287
287
  if ensure_exists:
288
- mkdir_if_not_exists(save_dir)
288
+ try:
289
+ mkdir_if_not_exists(save_dir)
290
+ except OSError as e:
291
+ if e.errno == 36:
292
+ # 目录名过长
293
+ limit = JmModuleConfig.VAR_FILE_NAME_LENGTH_LIMIT
294
+ jm_log('error', f'目录名过长,无法创建目录,强制缩短到{limit}个字符并重试')
295
+ save_dir = save_dir[0:limit]
296
+ mkdir_if_not_exists(save_dir)
289
297
 
290
298
  return save_dir
291
299
 
@@ -359,7 +367,7 @@ class JmOption:
359
367
  def deconstruct(self) -> Dict:
360
368
  return {
361
369
  'version': JmModuleConfig.JM_OPTION_VER,
362
- 'log': JmModuleConfig.flag_enable_jm_log,
370
+ 'log': JmModuleConfig.FLAG_ENABLE_JM_LOG,
363
371
  'dir_rule': {
364
372
  'rule': self.dir_rule.rule_dsl,
365
373
  'base_dir': self.dir_rule.base_dir,
@@ -376,6 +384,7 @@ class JmOption:
376
384
  @classmethod
377
385
  def from_file(cls, filepath: str) -> 'JmOption':
378
386
  dic: dict = PackerUtil.unpack(filepath)[0]
387
+ dic.setdefault('filepath', filepath)
379
388
  return cls.construct(dic)
380
389
 
381
390
  def to_file(self, filepath=None):
jmcomic/jm_plugin.py CHANGED
@@ -445,9 +445,7 @@ class ImageSuffixFilterPlugin(JmOptionPlugin):
445
445
  if image.img_file_suffix not in allowed_suffix_set:
446
446
  self.log(f'跳过下载图片: {image.tag},'
447
447
  f'因为其后缀\'{image.img_file_suffix}\'不在允许的后缀集合{allowed_suffix_set}内')
448
- # hook is_exists True to skip download
449
- image.is_exists = True
450
- return True
448
+ image.skip = True
451
449
 
452
450
  # let option decide
453
451
  return option_decide_cache(image)
@@ -484,7 +482,7 @@ class LogTopicFilterPlugin(JmOptionPlugin):
484
482
  if whitelist is not None:
485
483
  whitelist = set(whitelist)
486
484
 
487
- old_jm_log = JmModuleConfig.executor_log
485
+ old_jm_log = JmModuleConfig.EXECUTOR_LOG
488
486
 
489
487
  def new_jm_log(topic, msg):
490
488
  if whitelist is not None and topic not in whitelist:
@@ -492,7 +490,7 @@ class LogTopicFilterPlugin(JmOptionPlugin):
492
490
 
493
491
  old_jm_log(topic, msg)
494
492
 
495
- JmModuleConfig.executor_log = new_jm_log
493
+ JmModuleConfig.EXECUTOR_LOG = new_jm_log
496
494
 
497
495
 
498
496
  class AutoSetBrowserCookiesPlugin(JmOptionPlugin):
@@ -903,3 +901,94 @@ class JmServerPlugin(JmOptionPlugin):
903
901
  instance = JmServerPlugin(option)
904
902
  setattr(cls, field_name, instance)
905
903
  return instance
904
+
905
+
906
+ class SubscribeAlbumUpdatePlugin(JmOptionPlugin):
907
+ plugin_key = 'subscribe_album_update'
908
+
909
+ def invoke(self,
910
+ album_photo_dict=None,
911
+ email_notify=None,
912
+ download_if_has_update=True,
913
+ auto_update_after_download=True,
914
+ ) -> None:
915
+ if album_photo_dict is None:
916
+ return
917
+
918
+ album_photo_dict: Dict
919
+ for album_id, photo_id in album_photo_dict.copy().items():
920
+ # check update
921
+ try:
922
+ has_update, photo_new_list = self.check_photo_update(album_id, photo_id)
923
+ except JmcomicException as e:
924
+ self.log('Exception happened: ' + str(e), 'check_update.error')
925
+ continue
926
+
927
+ if has_update is False:
928
+ continue
929
+
930
+ self.log(f'album={album_id},发现新章节: {photo_new_list},准备开始下载')
931
+
932
+ # send email
933
+ try:
934
+ if email_notify:
935
+ SendQQEmailPlugin.build(self.option).invoke(**email_notify)
936
+ except PluginValidationException:
937
+ # ignore
938
+ pass
939
+
940
+ # download new photo
941
+ if has_update and download_if_has_update:
942
+ self.option.download_photo(photo_new_list)
943
+
944
+ if auto_update_after_download:
945
+ album_photo_dict[album_id] = photo_new_list[-1]
946
+ self.option.to_file()
947
+
948
+ def check_photo_update(self, album_id: str, photo_id: str):
949
+ client = self.option.new_jm_client()
950
+ album = client.get_album_detail(album_id)
951
+
952
+ photo_new_list = []
953
+ is_new_photo = False
954
+ sentinel = int(photo_id)
955
+
956
+ for photo in album:
957
+ if is_new_photo:
958
+ photo_new_list.append(photo.photo_id)
959
+
960
+ if int(photo.photo_id) == sentinel:
961
+ is_new_photo = True
962
+
963
+ return len(photo_new_list) != 0, photo_new_list
964
+
965
+
966
+ class SkipPhotoWithFewImagesPlugin(JmOptionPlugin):
967
+ plugin_key = 'skip_photo_with_few_images'
968
+
969
+ def invoke(self,
970
+ at_least_image_count: int,
971
+ photo: Optional[JmPhotoDetail] = None,
972
+ image: Optional[JmImageDetail] = None,
973
+ album: Optional[JmAlbumDetail] = None,
974
+ **kwargs
975
+ ):
976
+ self.try_mark_photo_skip_and_log(photo, at_least_image_count)
977
+ if image is not None:
978
+ self.try_mark_photo_skip_and_log(image.from_photo, at_least_image_count)
979
+
980
+ def try_mark_photo_skip_and_log(self, photo: JmPhotoDetail, at_least_image_count: int):
981
+ if photo is None:
982
+ return
983
+
984
+ if len(photo) >= at_least_image_count:
985
+ return
986
+
987
+ self.log(f'跳过下载章节: {photo.id} ({photo.album_id}[{photo.index}/{len(photo.from_album)}]),'
988
+ f'因为其图片数: {len(photo)} < {at_least_image_count} (at_least_image_count)')
989
+ photo.skip = True
990
+
991
+ @classmethod
992
+ @field_cache() # 单例
993
+ def build(cls, option: JmOption) -> 'JmOptionPlugin':
994
+ return super().build(option)
jmcomic/jm_toolkit.py CHANGED
@@ -472,7 +472,7 @@ class JmPageTool:
472
472
  return JmFavoritePage(content, folder_list, total)
473
473
 
474
474
  @classmethod
475
- def parse_api_to_search_page(cls, data: AdvancedEasyAccessDict) -> JmSearchPage:
475
+ def parse_api_to_search_page(cls, data: AdvancedDict) -> JmSearchPage:
476
476
  """
477
477
  model_data: {
478
478
  "search_query": "MANA",
@@ -501,7 +501,7 @@ class JmPageTool:
501
501
  return JmSearchPage(content, total)
502
502
 
503
503
  @classmethod
504
- def parse_api_to_favorite_page(cls, data: AdvancedEasyAccessDict) -> JmFavoritePage:
504
+ def parse_api_to_favorite_page(cls, data: AdvancedDict) -> JmFavoritePage:
505
505
  """
506
506
  {
507
507
  "list": [
@@ -546,7 +546,7 @@ class JmPageTool:
546
546
 
547
547
  @classmethod
548
548
  def adapt_content(cls, content):
549
- def adapt_item(item: AdvancedEasyAccessDict):
549
+ def adapt_item(item: AdvancedDict):
550
550
  item: dict = item.src_dict
551
551
  item.setdefault('tags', [])
552
552
  return item
@@ -673,7 +673,7 @@ class JmApiAdaptTool:
673
673
  series = data['series']
674
674
  episode_list = []
675
675
  for chapter in series:
676
- chapter = AdvancedEasyAccessDict(chapter)
676
+ chapter = AdvancedDict(chapter)
677
677
  # photo_id, photo_index, photo_title, photo_pub_date
678
678
  episode_list.append(
679
679
  (chapter.id, chapter.sort, chapter.name, None)
@@ -688,7 +688,7 @@ class JmApiAdaptTool:
688
688
  sort = 1
689
689
  series: list = data['series'] # series中的sort从1开始
690
690
  for chapter in series:
691
- chapter = AdvancedEasyAccessDict(chapter)
691
+ chapter = AdvancedDict(chapter)
692
692
  if int(chapter.id) == int(data['id']):
693
693
  sort = chapter.sort
694
694
  break
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jmcomic
3
- Version: 2.5.7
3
+ Version: 2.5.9
4
4
  Summary: Python API For JMComic (禁漫天堂)
5
5
  Home-page: https://github.com/hect0x7/JMComic-Crawler-Python
6
6
  Author: hect0x7
@@ -20,8 +20,8 @@ Classifier: Operating System :: Microsoft :: Windows
20
20
  Requires-Python: >=3.7
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
- Requires-Dist: commonX >=0.6.4
24
23
  Requires-Dist: curl-cffi
24
+ Requires-Dist: commonX
25
25
  Requires-Dist: PyYAML
26
26
  Requires-Dist: Pillow
27
27
  Requires-Dist: pycryptodome
@@ -0,0 +1,18 @@
1
+ jmcomic/__init__.py,sha256=WAC5HDkp2E-1HtrXfYzNJf9HWoCXqZx5wyLRUdFsFKI,902
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=f-2pFwJUzEdH4GVG8ExbR0dSoIP4w5M6CjX_Mlbxglc,17234
6
+ jmcomic/jm_config.py,sha256=zVyxJrB-KpSwrVCEooglIdC2fT6St08P2ycK3GrqDTE,15249
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=Y_ckE9xa6y0oqjG6CbKR3QX4TWvLuHK2ue_RzQkMxHM,22395
11
+ jmcomic/jm_plugin.py,sha256=YWt7MAWuy2zOsHEARDKf4ZFE3pDmHPqM1M0vwnoNgVg,32262
12
+ jmcomic/jm_toolkit.py,sha256=WLHaigCN03Z4b5swGTRTTSWzZsa0uFxGAnw4xKhbh1U,28649
13
+ jmcomic-2.5.9.dist-info/LICENSE,sha256=kz4coTxZxuGxisK3W00tjK57Zh3RcMGq-EnbXrK7-xA,1064
14
+ jmcomic-2.5.9.dist-info/METADATA,sha256=317JlniKqNOIcwLCBrxP5hUpMCBIoiCECPM9poW_OHs,6122
15
+ jmcomic-2.5.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
16
+ jmcomic-2.5.9.dist-info/entry_points.txt,sha256=tRbQltaGSBjejI0c9jYt-4SXQMd5nSDHcMvHmuTy4ow,44
17
+ jmcomic-2.5.9.dist-info/top_level.txt,sha256=puvVMFYJqIbd6NOTMEvOyugMTT8woBfSQyxEBan3zY4,8
18
+ jmcomic-2.5.9.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,18 +0,0 @@
1
- jmcomic/__init__.py,sha256=2g14LZxaEe5ZJkxNbXTZsHPPLGY0yDeUw0N_ig7ZSmQ,902
2
- jmcomic/api.py,sha256=uLHtSof7ZaiWZcNDDAz6grD1NYs4VxsoCOrsge3G7v8,3864
3
- jmcomic/cl.py,sha256=PBSh0JndNFZw3B7WJPj5Y8SeFdKzHE00jIwYo9An-K0,3475
4
- jmcomic/jm_client_impl.py,sha256=EkZ47CWDHUHGKoGAEupzKiElJu_m1BCaM03tQ3HGw1Q,37811
5
- jmcomic/jm_client_interface.py,sha256=4QkW3X3cXALWIHJmOy4MgO-IzPHuGIceqLTpVWG0vtE,17274
6
- jmcomic/jm_config.py,sha256=8hpToUislTRW5C9RKyANynvb1_9LELL3GuPYMQQSGT4,15103
7
- jmcomic/jm_downloader.py,sha256=-3YHaQqIk6z8eUOQVDH6gIptHbYjKxAiYpOwLZvVZMo,10279
8
- jmcomic/jm_entity.py,sha256=A9xXjPrp2CtnHy_RmYkFIE-YkhDov_6fxFQaQsa3z8s,18851
9
- jmcomic/jm_exception.py,sha256=6qDnPo-O6wqNBVweW-rdvMpwnvGCj2hapcLlZqTtTlg,4847
10
- jmcomic/jm_option.py,sha256=gOS8Nmz02MUd84gVI3pba2SSu9oZdIjYiZBaQqySUrA,21965
11
- jmcomic/jm_plugin.py,sha256=zt377h5Ey5BJuCetC0gfNjYHKj6s4H7L9xjihy_g0XY,29260
12
- jmcomic/jm_toolkit.py,sha256=9DbQIhTbA3_EijXXPIcE6GHWCSwOZrGepMbevxn0nqk,28699
13
- jmcomic-2.5.7.dist-info/LICENSE,sha256=kz4coTxZxuGxisK3W00tjK57Zh3RcMGq-EnbXrK7-xA,1064
14
- jmcomic-2.5.7.dist-info/METADATA,sha256=dKD08LcCd_TPWf_nSBhdML39i-UG1Diqfsv2tSz6JoM,6130
15
- jmcomic-2.5.7.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
16
- jmcomic-2.5.7.dist-info/entry_points.txt,sha256=tRbQltaGSBjejI0c9jYt-4SXQMd5nSDHcMvHmuTy4ow,44
17
- jmcomic-2.5.7.dist-info/top_level.txt,sha256=puvVMFYJqIbd6NOTMEvOyugMTT8woBfSQyxEBan3zY4,8
18
- jmcomic-2.5.7.dist-info/RECORD,,