1
1
from .jm_option import *
2
2
3
3
4
+ def catch_exception (func ):
5
+ from functools import wraps
6
+
7
+ @wraps (func )
8
+ def wrapper (self , * args , ** kwargs ):
9
+ self : JmDownloader
10
+ try :
11
+ return func (self , * args , ** kwargs )
12
+ except Exception as e :
13
+ detail : JmBaseEntity = args [0 ]
14
+ if detail .is_image ():
15
+ detail : JmImageDetail
16
+ jm_log ('image.failed' , f'图片下载失败: [{ detail .download_url } ], 异常: [{ e } ]' )
17
+ self .download_failed_image .append ((detail , e ))
18
+
19
+ elif detail .is_photo ():
20
+ detail : JmPhotoDetail
21
+ jm_log ('photo.failed' , f'章节下载失败: [{ detail .id } ], 异常: [{ e } ]' )
22
+ self .download_failed_photo .append ((detail , e ))
23
+
24
+ raise e
25
+
26
+ return wrapper
27
+
28
+
4
29
# noinspection PyMethodMayBeStatic
5
30
class DownloadCallback :
6
31
@@ -50,48 +75,50 @@ class JmDownloader(DownloadCallback):
50
75
51
76
def __init__ (self , option : JmOption ) -> None :
52
77
self .option = option
78
+ self .client = option .build_jm_client ()
53
79
# 下载成功的记录dict
54
80
self .download_success_dict : Dict [JmAlbumDetail , Dict [JmPhotoDetail , List [Tuple [str , JmImageDetail ]]]] = {}
55
81
# 下载失败的记录list
56
- self .download_failed_list : List [Tuple [JmImageDetail , BaseException ]] = []
82
+ self .download_failed_image : List [Tuple [JmImageDetail , BaseException ]] = []
83
+ self .download_failed_photo : List [Tuple [JmPhotoDetail , BaseException ]] = []
57
84
58
85
def download_album (self , album_id ):
59
- client = self .client_for_album (album_id )
60
- album = client .get_album_detail (album_id )
61
- self .download_by_album_detail (album , client )
86
+ album = self .client .get_album_detail (album_id )
87
+ self .download_by_album_detail (album )
62
88
return album
63
89
64
- def download_by_album_detail (self , album : JmAlbumDetail , client : JmcomicClient ):
90
+ def download_by_album_detail (self , album : JmAlbumDetail ):
65
91
self .before_album (album )
66
92
if album .skip :
67
93
return
68
- self .execute_by_condition (
94
+ self .execute_on_condition (
69
95
iter_objs = album ,
70
- apply = lambda photo : self .download_by_photo_detail ( photo , client ) ,
96
+ apply = self .download_by_photo_detail ,
71
97
count_batch = self .option .decide_photo_batch_count (album )
72
98
)
73
99
self .after_album (album )
74
100
75
101
def download_photo (self , photo_id ):
76
- client = self .client_for_photo (photo_id )
77
- photo = client .get_photo_detail (photo_id )
78
- self .download_by_photo_detail (photo , client )
102
+ photo = self .client .get_photo_detail (photo_id )
103
+ self .download_by_photo_detail (photo )
79
104
return photo
80
105
81
- def download_by_photo_detail (self , photo : JmPhotoDetail , client : JmcomicClient ):
82
- client .check_photo (photo )
106
+ @catch_exception
107
+ def download_by_photo_detail (self , photo : JmPhotoDetail ):
108
+ self .client .check_photo (photo )
83
109
84
110
self .before_photo (photo )
85
111
if photo .skip :
86
112
return
87
- self .execute_by_condition (
113
+ self .execute_on_condition (
88
114
iter_objs = photo ,
89
- apply = lambda image : self .download_by_image_detail ( image , client ) ,
115
+ apply = self .download_by_image_detail ,
90
116
count_batch = self .option .decide_image_batch_count (photo )
91
117
)
92
118
self .after_photo (photo )
93
119
94
- def download_by_image_detail (self , image : JmImageDetail , client : JmcomicClient ):
120
+ @catch_exception
121
+ def download_by_image_detail (self , image : JmImageDetail ):
95
122
img_save_path = self .option .decide_image_filepath (image )
96
123
97
124
image .save_path = img_save_path
@@ -110,22 +137,15 @@ def download_by_image_detail(self, image: JmImageDetail, client: JmcomicClient):
110
137
if use_cache is True and image .exists :
111
138
return
112
139
113
- try :
114
- client .download_by_image_detail (
115
- image ,
116
- img_save_path ,
117
- decode_image = decode_image ,
118
- )
119
- except BaseException as e :
120
- jm_log ('image.failed' , f'图片下载失败: [{ image .download_url } ], 异常: { e } ' )
121
- # 保存失败记录
122
- self .download_failed_list .append ((image , e ))
123
- raise
140
+ self .client .download_by_image_detail (
141
+ image ,
142
+ img_save_path ,
143
+ decode_image = decode_image ,
144
+ )
124
145
125
146
self .after_image (image , img_save_path )
126
147
127
- # noinspection PyMethodMayBeStatic
128
- def execute_by_condition (self ,
148
+ def execute_on_condition (self ,
129
149
iter_objs : DetailEntity ,
130
150
apply : Callable ,
131
151
count_batch : int ,
@@ -166,20 +186,6 @@ def do_filter(self, detail: DetailEntity):
166
186
"""
167
187
return detail
168
188
169
- # noinspection PyUnusedLocal
170
- def client_for_album (self , jm_album_id ) -> JmcomicClient :
171
- """
172
- 默认情况下,所有的JmDownloader共用一个JmcomicClient
173
- """
174
- return self .option .build_jm_client ()
175
-
176
- # noinspection PyUnusedLocal
177
- def client_for_photo (self , jm_photo_id ) -> JmcomicClient :
178
- """
179
- 默认情况下,所有的JmDownloader共用一个JmcomicClient
180
- """
181
- return self .option .build_jm_client ()
182
-
183
189
@property
184
190
def all_success (self ) -> bool :
185
191
"""
@@ -189,7 +195,7 @@ def all_success(self) -> bool:
189
195
190
196
注意!如果使用了filter机制,例如通过filter只下载3张图片,那么all_success也会为False
191
197
"""
192
- if len ( self .download_failed_list ) != 0 :
198
+ if self .has_download_failures :
193
199
return False
194
200
195
201
for album , photo_dict in self .download_success_dict .items ():
@@ -202,6 +208,10 @@ def all_success(self) -> bool:
202
208
203
209
return True
204
210
211
+ @property
212
+ def has_download_failures (self ):
213
+ return len (self .download_failed_image ) != 0 or len (self .download_failed_photo ) != 0
214
+
205
215
# 下面是回调方法
206
216
207
217
def before_album (self , album : JmAlbumDetail ):
@@ -259,6 +269,23 @@ def after_image(self, image: JmImageDetail, img_save_path):
259
269
downloader = self ,
260
270
)
261
271
272
+ def raise_if_has_exception (self ):
273
+ if not self .has_download_failures :
274
+ return
275
+ msg_ls = ['部分下载失败' , '' , '' ]
276
+
277
+ if len (self .download_failed_photo ) != 0 :
278
+ msg_ls [1 ] = f'共{ len (self .download_failed_photo )} 个章节下载失败: { self .download_failed_photo } '
279
+
280
+ if len (self .download_failed_image ) != 0 :
281
+ msg_ls [2 ] = f'共{ len (self .download_failed_image )} 个图片下载失败: { self .download_failed_image } '
282
+
283
+ ExceptionTool .raises (
284
+ '\n ' .join (msg_ls ),
285
+ {'downloader' : self },
286
+ PartialDownloadFailedException ,
287
+ )
288
+
262
289
# 下面是对with语法的支持
263
290
264
291
def __enter__ (self ):
@@ -283,7 +310,7 @@ class DoNotDownloadImage(JmDownloader):
283
310
不会下载任何图片的Downloader,用作测试
284
311
"""
285
312
286
- def download_by_image_detail (self , image : JmImageDetail , client : JmcomicClient ):
313
+ def download_by_image_detail (self , image : JmImageDetail ):
287
314
# ensure make dir
288
315
self .option .decide_image_filepath (image )
289
316
@@ -297,12 +324,13 @@ class JustDownloadSpecificCountImage(JmDownloader):
297
324
count_lock = Lock ()
298
325
count = 0
299
326
300
- def download_by_image_detail (self , image : JmImageDetail , client : JmcomicClient ):
327
+ @catch_exception
328
+ def download_by_image_detail (self , image : JmImageDetail ):
301
329
# ensure make dir
302
330
self .option .decide_image_filepath (image )
303
331
304
332
if self .try_countdown ():
305
- return super ().download_by_image_detail (image , client )
333
+ return super ().download_by_image_detail (image )
306
334
307
335
def try_countdown (self ):
308
336
if self .count < 0 :
0 commit comments