jmcomic 2.5.15__tar.gz → 2.5.17__tar.gz

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.
Files changed (23) hide show
  1. {jmcomic-2.5.15/src/jmcomic.egg-info → jmcomic-2.5.17}/PKG-INFO +1 -1
  2. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic/__init__.py +1 -1
  3. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic/api.py +1 -0
  4. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic/jm_client_impl.py +20 -10
  5. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic/jm_config.py +44 -36
  6. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic/jm_entity.py +10 -5
  7. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic/jm_option.py +13 -13
  8. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic/jm_plugin.py +67 -17
  9. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic/jm_toolkit.py +13 -0
  10. {jmcomic-2.5.15 → jmcomic-2.5.17/src/jmcomic.egg-info}/PKG-INFO +1 -1
  11. {jmcomic-2.5.15 → jmcomic-2.5.17}/LICENSE +0 -0
  12. {jmcomic-2.5.15 → jmcomic-2.5.17}/README.md +0 -0
  13. {jmcomic-2.5.15 → jmcomic-2.5.17}/setup.cfg +0 -0
  14. {jmcomic-2.5.15 → jmcomic-2.5.17}/setup.py +0 -0
  15. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic/cl.py +0 -0
  16. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic/jm_client_interface.py +0 -0
  17. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic/jm_downloader.py +0 -0
  18. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic/jm_exception.py +0 -0
  19. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic.egg-info/SOURCES.txt +0 -0
  20. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic.egg-info/dependency_links.txt +0 -0
  21. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic.egg-info/entry_points.txt +0 -0
  22. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic.egg-info/requires.txt +0 -0
  23. {jmcomic-2.5.15 → jmcomic-2.5.17}/src/jmcomic.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jmcomic
3
- Version: 2.5.15
3
+ Version: 2.5.17
4
4
  Summary: Python API For JMComic (禁漫天堂)
5
5
  Home-page: https://github.com/hect0x7/JMComic-Crawler-Python
6
6
  Author: hect0x7
@@ -2,7 +2,7 @@
2
2
  # 被依赖方 <--- 使用方
3
3
  # config <--- entity <--- toolkit <--- client <--- option <--- downloader
4
4
 
5
- __version__ = '2.5.15'
5
+ __version__ = '2.5.17'
6
6
 
7
7
  from .api import *
8
8
  from .jm_plugin import *
@@ -2,6 +2,7 @@ from .jm_downloader import *
2
2
 
3
3
  __DOWNLOAD_API_RET = Tuple[JmAlbumDetail, JmDownloader]
4
4
 
5
+
5
6
  def download_batch(download_api,
6
7
  jm_id_iter: Union[Iterable, Generator],
7
8
  option=None,
@@ -27,7 +27,7 @@ class AbstractJmClient(
27
27
  self.retry_times = retry_times
28
28
  self.domain_list = domain_list
29
29
  self.CLIENT_CACHE = None
30
- self.__username = None # help for favorite_folder method
30
+ self._username = None # help for favorite_folder method
31
31
  self.enable_cache()
32
32
  self.after_init()
33
33
 
@@ -92,7 +92,7 @@ class AbstractJmClient(
92
92
  jm_log(self.log_topic(), self.decode(url))
93
93
  else:
94
94
  # 图片url
95
- pass
95
+ self.update_request_with_specify_domain(kwargs, None, True)
96
96
 
97
97
  if domain_index != 0 or retry_count != 0:
98
98
  jm_log(f'req.retry',
@@ -133,7 +133,7 @@ class AbstractJmClient(
133
133
  """
134
134
  return resp
135
135
 
136
- def update_request_with_specify_domain(self, kwargs: dict, domain: str):
136
+ def update_request_with_specify_domain(self, kwargs: dict, domain: Optional[str], is_image: bool = False):
137
137
  """
138
138
  域名自动切换时,用于更新请求参数的回调
139
139
  """
@@ -412,7 +412,7 @@ class JmHtmlClient(AbstractJmClient):
412
412
  return resp
413
413
 
414
414
  self['cookies'] = new_cookies
415
- self.__username = username
415
+ self._username = username
416
416
 
417
417
  return resp
418
418
 
@@ -423,8 +423,8 @@ class JmHtmlClient(AbstractJmClient):
423
423
  username='',
424
424
  ) -> JmFavoritePage:
425
425
  if username == '':
426
- ExceptionTool.require_true(self.__username is not None, 'favorite_folder方法需要传username参数')
427
- username = self.__username
426
+ ExceptionTool.require_true(self._username is not None, 'favorite_folder方法需要传username参数')
427
+ username = self._username
428
428
 
429
429
  resp = self.get_jm_html(
430
430
  f'/user/{username}/favorite/albums',
@@ -463,7 +463,10 @@ class JmHtmlClient(AbstractJmClient):
463
463
 
464
464
  return resp
465
465
 
466
- def update_request_with_specify_domain(self, kwargs: dict, domain: Optional[str]):
466
+ def update_request_with_specify_domain(self, kwargs: dict, domain: Optional[str], is_image=False):
467
+ if is_image:
468
+ return
469
+
467
470
  latest_headers = kwargs.get('headers', None)
468
471
  base_headers = self.get_meta_data('headers', None) or JmModuleConfig.new_html_headers(domain)
469
472
  base_headers.update(latest_headers or {})
@@ -909,8 +912,10 @@ class JmApiClient(AbstractJmClient):
909
912
 
910
913
  return resp
911
914
 
912
- def update_request_with_specify_domain(self, kwargs: dict, domain: str):
913
- pass
915
+ def update_request_with_specify_domain(self, kwargs: dict, domain: Optional[str], is_image=False):
916
+ if is_image:
917
+ # 设置APP端的图片请求headers
918
+ kwargs['headers'] = {**JmModuleConfig.APP_HEADERS_TEMPLATE, **JmModuleConfig.APP_HEADERS_IMAGE}
914
919
 
915
920
  # noinspection PyMethodMayBeStatic
916
921
  def decide_headers_and_ts(self, kwargs, url):
@@ -930,7 +935,7 @@ class JmApiClient(AbstractJmClient):
930
935
  token, tokenparam = JmCryptoTool.token_and_tokenparam(ts)
931
936
 
932
937
  # 设置headers
933
- headers = kwargs.get('headers', None) or JmMagicConstants.APP_HEADERS_TEMPLATE.copy()
938
+ headers = kwargs.get('headers', None) or JmModuleConfig.APP_HEADERS_TEMPLATE.copy()
934
939
  headers.update({
935
940
  'token': token,
936
941
  'tokenparam': tokenparam,
@@ -973,6 +978,11 @@ class JmApiClient(AbstractJmClient):
973
978
  # 例如图片请求
974
979
  return resp
975
980
 
981
+ code = resp.status_code
982
+ if code >= 500:
983
+ msg = JmModuleConfig.JM_ERROR_STATUS_CODE.get(code, f'HTTP状态码: {code}')
984
+ ExceptionTool.raises_resp(f"禁漫API异常响应, {msg}", resp)
985
+
976
986
  url = resp.request.url
977
987
 
978
988
  if self.API_SCRAMBLE in url:
@@ -59,50 +59,16 @@ class JmMagicConstants:
59
59
  SUB_SINGLE_JAPANESE = SUB_JAPANESE
60
60
  SUB_SINGLE_YOUTH = 'youth'
61
61
 
62
- # 分页大小
63
- PAGE_SIZE_SEARCH = 80
64
- PAGE_SIZE_FAVORITE = 20
65
-
66
62
  # 图片分割参数
67
63
  SCRAMBLE_220980 = 220980
68
64
  SCRAMBLE_268850 = 268850
69
65
  SCRAMBLE_421926 = 421926 # 2023-02-08后改了图片切割算法
70
66
 
71
- # 当本子没有作者名字时,顶替作者名字
72
- DEFAULT_AUTHOR = 'default_author'
73
-
74
67
  # 移动端API密钥
75
68
  APP_TOKEN_SECRET = '18comicAPP'
76
69
  APP_TOKEN_SECRET_2 = '18comicAPPContent'
77
70
  APP_DATA_SECRET = '185Hcomic3PAPP7R'
78
- APP_VERSION = '1.7.0'
79
- APP_HEADERS_TEMPLATE = {
80
- 'Accept-Encoding': 'gzip',
81
- 'user-agent': 'Mozilla/5.0 (Linux; Android 9; V1938CT Build/PQ3A.190705.11211812; wv) AppleWebKit/537.36 (KHTML, '
82
- 'like Gecko) Version/4.0 Chrome/91.0.4472.114 Safari/537.36',
83
- }
84
-
85
- # 网页端headers
86
- HTML_HEADERS_TEMPLATE = {
87
- 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,'
88
- 'application/signed-exchange;v=b3;q=0.7',
89
- 'accept-language': 'zh-CN,zh;q=0.9',
90
- 'cache-control': 'no-cache',
91
- 'dnt': '1',
92
- 'pragma': 'no-cache',
93
- 'priority': 'u=0, i',
94
- 'referer': 'https://18comic.vip/',
95
- 'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
96
- 'sec-ch-ua-mobile': '?0',
97
- 'sec-ch-ua-platform': '"Windows"',
98
- 'sec-fetch-dest': 'document',
99
- 'sec-fetch-mode': 'navigate',
100
- 'sec-fetch-site': 'none',
101
- 'sec-fetch-user': '?1',
102
- 'upgrade-insecure-requests': '1',
103
- 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 '
104
- 'Safari/537.36',
105
- }
71
+ APP_VERSION = '1.7.1'
106
72
 
107
73
 
108
74
  # 模块级别共用配置
@@ -123,13 +89,55 @@ class JmModuleConfig:
123
89
  # JM的异常网页code
124
90
  JM_ERROR_STATUS_CODE = {
125
91
  403: 'ip地区禁止访问/爬虫被识别',
92
+ 500: '500: 禁漫服务器内部异常(可能是服务器过载,可以切换ip或稍后重试)',
126
93
  520: '520: Web server is returning an unknown error (禁漫服务器内部报错)',
127
94
  524: '524: The origin web server timed out responding to this request. (禁漫服务器处理超时)',
128
95
  }
129
96
 
97
+ # 分页大小
98
+ PAGE_SIZE_SEARCH = 80
99
+ PAGE_SIZE_FAVORITE = 20
100
+
130
101
  # 图片分隔相关
131
102
  SCRAMBLE_CACHE = {}
132
103
 
104
+ # 当本子没有作者名字时,顶替作者名字
105
+ DEFAULT_AUTHOR = 'default_author'
106
+
107
+ APP_HEADERS_TEMPLATE = {
108
+ 'Accept-Encoding': 'gzip, deflate',
109
+ 'user-agent': 'Mozilla/5.0 (Linux; Android 9; V1938CT Build/PQ3A.190705.11211812; wv) AppleWebKit/537.36 (KHTML, '
110
+ 'like Gecko) Version/4.0 Chrome/91.0.4472.114 Safari/537.36',
111
+ }
112
+
113
+ APP_HEADERS_IMAGE = {
114
+ 'Accept': 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
115
+ 'X-Requested-With': 'com.jiaohua_browser',
116
+ 'Referer': 'https://www.jmfreedomproxy.xyz/',
117
+ 'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7',
118
+ }
119
+
120
+ # 网页端headers
121
+ HTML_HEADERS_TEMPLATE = {
122
+ 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,'
123
+ 'application/signed-exchange;v=b3;q=0.7',
124
+ 'accept-language': 'zh-CN,zh;q=0.9',
125
+ 'cache-control': 'no-cache',
126
+ 'dnt': '1',
127
+ 'pragma': 'no-cache',
128
+ 'priority': 'u=0, i',
129
+ 'referer': 'https://18comic.vip/',
130
+ 'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
131
+ 'sec-ch-ua-mobile': '?0',
132
+ 'sec-ch-ua-platform': '"Windows"',
133
+ 'sec-fetch-dest': 'document',
134
+ 'sec-fetch-mode': 'navigate',
135
+ 'sec-fetch-site': 'none',
136
+ 'sec-fetch-user': '?1',
137
+ 'upgrade-insecure-requests': '1',
138
+ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 '
139
+ 'Safari/537.36',
140
+ }
133
141
  # cookies,目前只在移动端使用,因为移动端请求接口须携带,但不会校验cookies的内容。
134
142
  APP_COOKIES = None
135
143
 
@@ -335,7 +343,7 @@ class JmModuleConfig:
335
343
  """
336
344
  网页端的headers
337
345
  """
338
- headers = JmMagicConstants.HTML_HEADERS_TEMPLATE.copy()
346
+ headers = cls.HTML_HEADERS_TEMPLATE.copy()
339
347
  headers.update({
340
348
  'authority': domain,
341
349
  'origin': f'https://{domain}',
@@ -1,3 +1,5 @@
1
+ from functools import lru_cache
2
+
1
3
  from common import *
2
4
 
3
5
  from .jm_config import *
@@ -303,7 +305,8 @@ class JmPhotoDetail(DetailEntity, Downloadable):
303
305
  # 2. 值目前在网页端只在photo页面的图片标签的data-original属性出现
304
306
  # 这里的模拟思路是,获取到第一个图片标签的data-original,
305
307
  # 取出其query参数 → self.data_original_query_params, 该值未来会传递给 JmImageDetail
306
- self.data_original_query_params = self.get_data_original_query_params(data_original_0)
308
+ # self.data_original_query_params = self.get_data_original_query_params(data_original_0)
309
+ self.data_original_query_params = None
307
310
 
308
311
  @property
309
312
  def is_single_album(self) -> bool:
@@ -353,7 +356,7 @@ class JmPhotoDetail(DetailEntity, Downloadable):
353
356
  return self._author.strip()
354
357
 
355
358
  # 使用默认
356
- return JmMagicConstants.DEFAULT_AUTHOR
359
+ return JmModuleConfig.DEFAULT_AUTHOR
357
360
 
358
361
  def create_image_detail(self, index) -> JmImageDetail:
359
362
  # 校验参数
@@ -400,6 +403,7 @@ class JmPhotoDetail(DetailEntity, Downloadable):
400
403
  def id(self):
401
404
  return self.photo_id
402
405
 
406
+ @lru_cache(None)
403
407
  def getindex(self, index) -> JmImageDetail:
404
408
  return self.create_image_detail(index)
405
409
 
@@ -472,7 +476,7 @@ class JmAlbumDetail(DetailEntity, Downloadable):
472
476
  if len(self.authors) >= 1:
473
477
  return self.authors[0]
474
478
 
475
- return JmMagicConstants.DEFAULT_AUTHOR
479
+ return JmModuleConfig.DEFAULT_AUTHOR
476
480
 
477
481
  @property
478
482
  def id(self):
@@ -514,6 +518,7 @@ class JmAlbumDetail(DetailEntity, Downloadable):
514
518
 
515
519
  return photo
516
520
 
521
+ @lru_cache(None)
517
522
  def getindex(self, item) -> JmPhotoDetail:
518
523
  return self.create_photo_detail(item)
519
524
 
@@ -608,7 +613,7 @@ class JmSearchPage(JmPageContent):
608
613
 
609
614
  @property
610
615
  def page_size(self) -> int:
611
- return JmMagicConstants.PAGE_SIZE_SEARCH
616
+ return JmModuleConfig.PAGE_SIZE_SEARCH
612
617
 
613
618
  # 下面的方法是对单个album的包装
614
619
 
@@ -649,7 +654,7 @@ class JmFavoritePage(JmPageContent):
649
654
 
650
655
  @property
651
656
  def page_size(self) -> int:
652
- return JmMagicConstants.PAGE_SIZE_FAVORITE
657
+ return JmModuleConfig.PAGE_SIZE_FAVORITE
653
658
 
654
659
  def iter_folder_id_name(self) -> Generator[Tuple[str, str], None, None]:
655
660
  """
@@ -270,15 +270,7 @@ class JmOption:
270
270
  )
271
271
 
272
272
  if ensure_exists:
273
- try:
274
- mkdir_if_not_exists(save_dir)
275
- except OSError as e:
276
- if e.errno == 36:
277
- # 目录名过长
278
- limit = JmModuleConfig.VAR_FILE_NAME_LENGTH_LIMIT
279
- jm_log('error', f'目录名过长,无法创建目录,强制缩短到{limit}个字符并重试')
280
- save_dir = save_dir[0:limit]
281
- mkdir_if_not_exists(save_dir)
273
+ save_dir = JmcomicText.try_mkdir(save_dir)
282
274
 
283
275
  return save_dir
284
276
 
@@ -517,13 +509,21 @@ class JmOption:
517
509
 
518
510
  # 下面的方法提供面向对象的调用风格
519
511
 
520
- def download_album(self, album_id):
512
+ def download_album(self,
513
+ album_id,
514
+ downloader=None,
515
+ callback=None,
516
+ ):
521
517
  from .api import download_album
522
- download_album(album_id, self)
518
+ download_album(album_id, self, downloader, callback)
523
519
 
524
- def download_photo(self, photo_id):
520
+ def download_photo(self,
521
+ photo_id,
522
+ downloader=None,
523
+ callback=None
524
+ ):
525
525
  from .api import download_photo
526
- download_photo(photo_id, self)
526
+ download_photo(photo_id, self, downloader, callback)
527
527
 
528
528
  # 下面的方法为调用插件提供支持
529
529
 
@@ -601,7 +601,7 @@ class FavoriteFolderExportPlugin(JmOptionPlugin):
601
601
  for page in page_data:
602
602
  for aid, extra in page.content:
603
603
  data.append(
604
- (aid, extra.get('author', '') or JmMagicConstants.DEFAULT_AUTHOR, extra['name'])
604
+ (aid, extra.get('author', '') or JmModuleConfig.DEFAULT_AUTHOR, extra['name'])
605
605
  )
606
606
 
607
607
  if len(data) == 0:
@@ -734,13 +734,17 @@ class Img2pdfPlugin(JmOptionPlugin):
734
734
  plugin_key = 'img2pdf'
735
735
 
736
736
  def invoke(self,
737
- photo: JmPhotoDetail,
737
+ photo: JmPhotoDetail = None,
738
+ album: JmAlbumDetail = None,
738
739
  downloader=None,
739
740
  pdf_dir=None,
740
741
  filename_rule='Pid',
741
742
  delete_original_file=False,
742
743
  **kwargs,
743
744
  ):
745
+ if photo is None and album is None:
746
+ jm_log('wrong_usage', 'img2pdf必须运行在after_photo或after_album时')
747
+
744
748
  try:
745
749
  import img2pdf
746
750
  except ImportError:
@@ -749,28 +753,50 @@ class Img2pdfPlugin(JmOptionPlugin):
749
753
 
750
754
  self.delete_original_file = delete_original_file
751
755
 
752
- # 处理文件夹配置
753
- filename = DirRule.apply_rule_directly(None, photo, filename_rule)
754
- photo_dir = self.option.decide_image_save_dir(photo)
755
-
756
756
  # 处理生成的pdf文件的路径
757
- if pdf_dir is None:
758
- pdf_dir = photo_dir
759
- else:
760
- pdf_dir = fix_filepath(pdf_dir, True)
761
- mkdir_if_not_exists(pdf_dir)
757
+ pdf_dir = self.ensure_make_pdf_dir(pdf_dir)
758
+
759
+ # 处理pdf文件名
760
+ filename = DirRule.apply_rule_directly(album, photo, filename_rule)
762
761
 
762
+ # pdf路径
763
763
  pdf_filepath = os.path.join(pdf_dir, f'{filename}.pdf')
764
764
 
765
765
  # 调用 img2pdf 把 photo_dir 下的所有图片转为pdf
766
- all_img = files_of_dir(photo_dir)
767
- with open(pdf_filepath, 'wb') as f:
768
- f.write(img2pdf.convert(all_img))
766
+ img_path_ls, img_dir_ls = self.write_img_2_pdf(pdf_filepath, album, photo)
767
+ self.log(f'Convert Successfully: JM{album or photo} → {pdf_filepath}')
769
768
 
770
769
  # 执行删除
771
- self.log(f'Convert Successfully: JM{photo.id} → {pdf_filepath}')
772
- all_img.append(self.option.decide_image_save_dir(photo, ensure_exists=False))
773
- self.execute_deletion(all_img)
770
+ img_path_ls += img_dir_ls
771
+ self.execute_deletion(img_path_ls)
772
+
773
+ def write_img_2_pdf(self, pdf_filepath, album: JmAlbumDetail, photo: JmPhotoDetail):
774
+ import img2pdf
775
+
776
+ if album is None:
777
+ img_dir_ls = [self.option.decide_image_save_dir(photo)]
778
+ else:
779
+ img_dir_ls = [self.option.decide_image_save_dir(photo) for photo in album]
780
+
781
+ img_path_ls = []
782
+
783
+ for img_dir in img_dir_ls:
784
+ imgs = files_of_dir(img_dir)
785
+ if not imgs:
786
+ continue
787
+ img_path_ls += imgs
788
+
789
+ with open(pdf_filepath, 'wb') as f:
790
+ f.write(img2pdf.convert(img_path_ls))
791
+
792
+ return img_path_ls, img_dir_ls
793
+
794
+ @staticmethod
795
+ def ensure_make_pdf_dir(pdf_dir: str):
796
+ pdf_dir = pdf_dir or os.getcwd()
797
+ pdf_dir = fix_filepath(pdf_dir, True)
798
+ mkdir_if_not_exists(pdf_dir)
799
+ return pdf_dir
774
800
 
775
801
 
776
802
  class JmServerPlugin(JmOptionPlugin):
@@ -1067,3 +1093,27 @@ class DeleteDuplicatedFilesPlugin(JmOptionPlugin):
1067
1093
  [f' {path}' for path in paths]
1068
1094
  self.log('\n'.join(message))
1069
1095
  self.execute_deletion(paths)
1096
+
1097
+
1098
+ class ReplacePathStringPlugin(JmOptionPlugin):
1099
+ plugin_key = 'replace_path_string'
1100
+
1101
+ def invoke(self,
1102
+ replace: Dict[str, str],
1103
+ ):
1104
+ if not replace:
1105
+ return
1106
+
1107
+ old_decide_dir = self.option.decide_image_save_dir
1108
+
1109
+ def new_decide_dir(photo, ensure_exists=True) -> str:
1110
+ original_path: str = old_decide_dir(photo, False)
1111
+ for k, v in replace.items():
1112
+ original_path = original_path.replace(k, v)
1113
+
1114
+ if ensure_exists:
1115
+ JmcomicText.try_mkdir(original_path)
1116
+
1117
+ return original_path
1118
+
1119
+ self.option.decide_image_save_dir = new_decide_dir
@@ -316,6 +316,19 @@ class JmcomicText:
316
316
  import zhconv
317
317
  return zhconv.convert(s, 'zh_cn')
318
318
 
319
+ @classmethod
320
+ def try_mkdir(cls, save_dir: str):
321
+ try:
322
+ mkdir_if_not_exists(save_dir)
323
+ except OSError as e:
324
+ if e.errno == 36:
325
+ # 目录名过长
326
+ limit = JmModuleConfig.VAR_FILE_NAME_LENGTH_LIMIT
327
+ jm_log('error', f'目录名过长,无法创建目录,强制缩短到{limit}个字符并重试')
328
+ save_dir = save_dir[0:limit]
329
+ mkdir_if_not_exists(save_dir)
330
+ return save_dir
331
+
319
332
 
320
333
  # 支持dsl: #{???} -> os.getenv(???)
321
334
  JmcomicText.dsl_replacer.add_dsl_and_replacer(r'\$\{(.*?)\}', JmcomicText.match_os_env)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jmcomic
3
- Version: 2.5.15
3
+ Version: 2.5.17
4
4
  Summary: Python API For JMComic (禁漫天堂)
5
5
  Home-page: https://github.com/hect0x7/JMComic-Crawler-Python
6
6
  Author: hect0x7
File without changes
File without changes
File without changes
File without changes
File without changes