Skip to content

Commit e99af0c

Browse files
committed
v2.5.35: 优化部分下载失败时异常处理
1 parent b02994d commit e99af0c

4 files changed

Lines changed: 68 additions & 73 deletions

File tree

src/jmcomic/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def download_album(jm_album_id,
7272
if callback is not None:
7373
callback(album, dler)
7474
if check_exception:
75-
dler.raise_if_have_exception()
75+
dler.raise_if_has_exception()
7676
return album, dler
7777

7878

src/jmcomic/jm_client_impl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ def check_special_text(cls, resp):
566566

567567
cls.raise_request_error(
568568
resp,
569-
f'{reason}'
569+
f'{reason}({content})'
570570
+ (f': {url}' if url is not None else '')
571571
)
572572

src/jmcomic/jm_downloader.py

Lines changed: 52 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
from .jm_option import *
22

33

4-
def catch_exception(field_name):
5-
def deco(func):
6-
def wrapper(self, *args, **kwargs):
7-
try:
8-
return func(self, *args, **kwargs)
9-
except Exception as e:
10-
detail: JmBaseEntity = args[1]
11-
getattr(self, field_name).append((detail, e))
12-
if detail.is_image():
13-
detail: JmImageDetail
14-
jm_log('image.failed', f'图片下载失败: [{detail.download_url}], 异常: {e}')
4+
def catch_exception(func):
5+
def wrapper(self, *args, **kwargs):
6+
self: JmDownloader
7+
try:
8+
return func(self, *args, **kwargs)
9+
except Exception as e:
10+
detail: JmBaseEntity = args[0]
11+
if detail.is_image():
12+
detail: JmImageDetail
13+
jm_log('image.failed', f'图片下载失败: [{detail.download_url}], 异常: [{e}]')
14+
self.download_failed_image.append((detail, e))
1515

16-
elif detail.is_photo():
17-
detail: JmPhotoDetail
18-
jm_log('photo.failed', f'章节下载失败: [{detail.id}], 异常: {e}')
16+
elif detail.is_photo():
17+
detail: JmPhotoDetail
18+
jm_log('photo.failed', f'章节下载失败: [{detail.id}], 异常: [{e}]')
19+
self.download_failed_photo.append((detail, e))
1920

20-
raise e
21+
raise e
2122

22-
return wrapper
23-
24-
return deco
23+
wrapper.__name__ = func.__name__
24+
return wrapper
2525

2626

2727
# noinspection PyMethodMayBeStatic
@@ -73,51 +73,50 @@ class JmDownloader(DownloadCallback):
7373

7474
def __init__(self, option: JmOption) -> None:
7575
self.option = option
76+
self.client = option.build_jm_client()
7677
# 下载成功的记录dict
7778
self.download_success_dict: Dict[JmAlbumDetail, Dict[JmPhotoDetail, List[Tuple[str, JmImageDetail]]]] = {}
7879
# 下载失败的记录list
7980
self.download_failed_image: List[Tuple[JmImageDetail, BaseException]] = []
8081
self.download_failed_photo: List[Tuple[JmPhotoDetail, BaseException]] = []
8182

8283
def download_album(self, album_id):
83-
client = self.client_for_album(album_id)
84-
album = client.get_album_detail(album_id)
85-
self.download_by_album_detail(album, client)
84+
album = self.client.get_album_detail(album_id)
85+
self.download_by_album_detail(album)
8686
return album
8787

88-
def download_by_album_detail(self, album: JmAlbumDetail, client: JmcomicClient):
88+
def download_by_album_detail(self, album: JmAlbumDetail):
8989
self.before_album(album)
9090
if album.skip:
9191
return
92-
self.execute_by_condition(
92+
self.execute_on_condition(
9393
iter_objs=album,
94-
apply=lambda photo: self.download_by_photo_detail(photo, client),
94+
apply=self.download_by_photo_detail,
9595
count_batch=self.option.decide_photo_batch_count(album)
9696
)
9797
self.after_album(album)
9898

9999
def download_photo(self, photo_id):
100-
client = self.client_for_photo(photo_id)
101-
photo = client.get_photo_detail(photo_id)
102-
self.download_by_photo_detail(photo, client)
100+
photo = self.client.get_photo_detail(photo_id)
101+
self.download_by_photo_detail(photo)
103102
return photo
104103

105-
@catch_exception('download_failed_photo')
106-
def download_by_photo_detail(self, photo: JmPhotoDetail, client: JmcomicClient):
107-
client.check_photo(photo)
104+
@catch_exception
105+
def download_by_photo_detail(self, photo: JmPhotoDetail):
106+
self.client.check_photo(photo)
108107

109108
self.before_photo(photo)
110109
if photo.skip:
111110
return
112-
self.execute_by_condition(
111+
self.execute_on_condition(
113112
iter_objs=photo,
114-
apply=lambda image: self.download_by_image_detail(image, client),
113+
apply=self.download_by_image_detail,
115114
count_batch=self.option.decide_image_batch_count(photo)
116115
)
117116
self.after_photo(photo)
118117

119-
@catch_exception('download_failed_image')
120-
def download_by_image_detail(self, image: JmImageDetail, client: JmcomicClient):
118+
@catch_exception
119+
def download_by_image_detail(self, image: JmImageDetail):
121120
img_save_path = self.option.decide_image_filepath(image)
122121

123122
image.save_path = img_save_path
@@ -136,16 +135,15 @@ def download_by_image_detail(self, image: JmImageDetail, client: JmcomicClient):
136135
if use_cache is True and image.exists:
137136
return
138137

139-
client.download_by_image_detail(
138+
self.client.download_by_image_detail(
140139
image,
141140
img_save_path,
142141
decode_image=decode_image,
143142
)
144143

145144
self.after_image(image, img_save_path)
146145

147-
# noinspection PyMethodMayBeStatic
148-
def execute_by_condition(self,
146+
def execute_on_condition(self,
149147
iter_objs: DetailEntity,
150148
apply: Callable,
151149
count_batch: int,
@@ -186,20 +184,6 @@ def do_filter(self, detail: DetailEntity):
186184
"""
187185
return detail
188186

189-
# noinspection PyUnusedLocal
190-
def client_for_album(self, jm_album_id) -> JmcomicClient:
191-
"""
192-
默认情况下,所有的JmDownloader共用一个JmcomicClient
193-
"""
194-
return self.option.build_jm_client()
195-
196-
# noinspection PyUnusedLocal
197-
def client_for_photo(self, jm_photo_id) -> JmcomicClient:
198-
"""
199-
默认情况下,所有的JmDownloader共用一个JmcomicClient
200-
"""
201-
return self.option.build_jm_client()
202-
203187
@property
204188
def all_success(self) -> bool:
205189
"""
@@ -209,7 +193,7 @@ def all_success(self) -> bool:
209193
210194
注意!如果使用了filter机制,例如通过filter只下载3张图片,那么all_success也会为False
211195
"""
212-
if not self.is_empty_download_failed:
196+
if not self.has_no_download_failed_exception:
213197
return False
214198

215199
for album, photo_dict in self.download_success_dict.items():
@@ -223,7 +207,7 @@ def all_success(self) -> bool:
223207
return True
224208

225209
@property
226-
def is_empty_download_failed(self):
210+
def has_no_download_failed_exception(self):
227211
return len(self.download_failed_image) == 0 and len(self.download_failed_photo) == 0
228212

229213
# 下面是回调方法
@@ -283,13 +267,19 @@ def after_image(self, image: JmImageDetail, img_save_path):
283267
downloader=self,
284268
)
285269

286-
def raise_if_have_exception(self):
287-
if self.is_empty_download_failed:
270+
def raise_if_has_exception(self):
271+
if self.has_no_download_failed_exception:
288272
return
273+
msg_ls = ['部分下载失败', '', '']
274+
275+
if len(self.download_failed_photo) != 0:
276+
msg_ls[1] = f'共{len(self.download_failed_photo)}个章节下载失败: {self.download_failed_photo}'
277+
278+
if len(self.download_failed_image) != 0:
279+
msg_ls[2] = f'共{len(self.download_failed_image)}个图片下载失败: {self.download_failed_image}'
280+
289281
ExceptionTool.raises(
290-
f'部分下载失败: 有{len(self.download_failed_photo)}个章节下载失败, {len(self.download_failed_image)}个图片下载失败。\n' +
291-
f'失败章节IDs: {[photo.id for photo, _ in self.download_failed_photo][:5]}{"..." if len(self.download_failed_photo) > 5 else ""}\n' +
292-
f'失败图片URLs: {[image.download_url for image, _ in self.download_failed_image][:5]}{"..." if len(self.download_failed_image) > 5 else ""}',
282+
'\n'.join(msg_ls),
293283
{'downloader': self},
294284
PartialDownloadFailedException,
295285
)
@@ -318,7 +308,7 @@ class DoNotDownloadImage(JmDownloader):
318308
不会下载任何图片的Downloader,用作测试
319309
"""
320310

321-
def download_by_image_detail(self, image: JmImageDetail, client: JmcomicClient):
311+
def download_by_image_detail(self, image: JmImageDetail):
322312
# ensure make dir
323313
self.option.decide_image_filepath(image)
324314

@@ -332,12 +322,13 @@ class JustDownloadSpecificCountImage(JmDownloader):
332322
count_lock = Lock()
333323
count = 0
334324

335-
def download_by_image_detail(self, image: JmImageDetail, client: JmcomicClient):
325+
@catch_exception
326+
def download_by_image_detail(self, image: JmImageDetail):
336327
# ensure make dir
337328
self.option.decide_image_filepath(image)
338329

339330
if self.try_countdown():
340-
return super().download_by_image_detail(image, client)
331+
return super().download_by_image_detail(image)
341332

342333
def try_countdown(self):
343334
if self.count < 0:

src/jmcomic/jm_entity.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ def is_album(cls):
3535
def is_page(cls):
3636
return False
3737

38+
@classmethod
39+
def __alias__(cls):
40+
# "JmAlbumDetail" -> "album" (本子)
41+
# "JmPhotoDetail" -> "photo" (章节)
42+
cls_name = cls.__name__
43+
return cls_name[cls_name.index("m") + 1: cls_name.rfind("Detail")].lower()
44+
3845

3946
class IndexedEntity:
4047
def getindex(self, index: int):
@@ -125,17 +132,9 @@ def idoname(self):
125132
return f'[{self.id}] {self.oname}'
126133

127134
def __str__(self):
128-
return f'{self.__class__.__name__}' \
129-
'{' \
130-
f'{self.id}: {self.title}' \
131-
'}'
135+
return f'''{self.__class__.__name__}({self.__alias__()}-{self.id}: "{self.title}")'''
132136

133-
@classmethod
134-
def __alias__(cls):
135-
# "JmAlbumDetail" -> "album" (本子)
136-
# "JmPhotoDetail" -> "photo" (章节)
137-
cls_name = cls.__name__
138-
return cls_name[cls_name.index("m") + 1: cls_name.rfind("Detail")].lower()
137+
__repr__ = __str__
139138

140139
@classmethod
141140
def get_dirname(cls, detail: 'DetailEntity', ref: str) -> str:
@@ -258,6 +257,11 @@ def tag(self) -> str:
258257
def is_image(cls):
259258
return True
260259

260+
def __str__(self):
261+
return f'''{self.__class__.__name__}({self.__alias__()}-[{self.download_url}])'''
262+
263+
__repr__ = __str__
264+
261265

262266
class JmPhotoDetail(DetailEntity, Downloadable):
263267

0 commit comments

Comments
 (0)