From 879cf04627bf8b045d860ae4fe9134b3d4ed1cd4 Mon Sep 17 00:00:00 2001 From: hect0x7 <93357912+hect0x7@users.noreply.github.com> Date: Tue, 8 Apr 2025 17:00:53 +0800 Subject: [PATCH 1/5] =?UTF-8?q?v2.5.35:=20=E5=AE=8C=E5=96=84=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E4=B8=8A=E6=8A=9B=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jmcomic/__init__.py | 2 +- src/jmcomic/api.py | 5 +++- src/jmcomic/jm_downloader.py | 55 +++++++++++++++++++++++++++--------- src/jmcomic/jm_exception.py | 7 +++-- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/jmcomic/__init__.py b/src/jmcomic/__init__.py index 93308a353..df2750cf8 100644 --- a/src/jmcomic/__init__.py +++ b/src/jmcomic/__init__.py @@ -2,7 +2,7 @@ # 被依赖方 <--- 使用方 # config <--- entity <--- toolkit <--- client <--- option <--- downloader -__version__ = '2.5.34' +__version__ = '2.5.35' from .api import * from .jm_plugin import * diff --git a/src/jmcomic/api.py b/src/jmcomic/api.py index 5f657a90b..aed4f120e 100644 --- a/src/jmcomic/api.py +++ b/src/jmcomic/api.py @@ -48,6 +48,7 @@ def download_album(jm_album_id, option=None, downloader=None, callback=None, + check_exception=True, ) -> Union[__DOWNLOAD_API_RET, Set[__DOWNLOAD_API_RET]]: """ 下载一个本子(album),包含其所有的章节(photo) @@ -58,6 +59,7 @@ def download_album(jm_album_id, :param option: 下载选项 :param downloader: 下载器类 :param callback: 返回值回调函数,可以拿到 album 和 downloader + :param check_exception: 是否检查异常, 如果为True,会检查downloader是否有下载异常,并上抛PartialDownloadFailedException :return: 对于的本子实体类,下载器(如果是上述的批量情况,返回值为download_batch的返回值) """ @@ -69,7 +71,8 @@ def download_album(jm_album_id, if callback is not None: callback(album, dler) - + if check_exception: + dler.raise_if_have_exception() return album, dler diff --git a/src/jmcomic/jm_downloader.py b/src/jmcomic/jm_downloader.py index 40df64ece..15391f70d 100644 --- a/src/jmcomic/jm_downloader.py +++ b/src/jmcomic/jm_downloader.py @@ -53,7 +53,31 @@ def __init__(self, option: JmOption) -> None: # 下载成功的记录dict self.download_success_dict: Dict[JmAlbumDetail, Dict[JmPhotoDetail, List[Tuple[str, JmImageDetail]]]] = {} # 下载失败的记录list - self.download_failed_list: List[Tuple[JmImageDetail, BaseException]] = [] + self.download_failed_image: List[Tuple[JmImageDetail, BaseException]] = [] + self.download_failed_photo: List[Tuple[JmPhotoDetail, BaseException]] = [] + + @staticmethod + def catch_exception(field_name): + def deco(func): + def wrapper(self, *args, **kwargs): + try: + return func(self, *args, **kwargs) + except Exception as e: + getattr(self, field_name).append(e) + detail: JmBaseEntity = args[1] + if detail.is_image(): + detail: JmImageDetail + jm_log('image.failed', f'图片下载失败: [{detail.download_url}], 异常: {e}') + + elif detail.is_photo(): + detail: JmPhotoDetail + jm_log('photo.failed', f'章节下载失败: [{detail.id}], 异常: {e}') + + raise e + + return wrapper + + return deco def download_album(self, album_id): client = self.client_for_album(album_id) @@ -78,6 +102,7 @@ def download_photo(self, photo_id): self.download_by_photo_detail(photo, client) return photo + @catch_exception('download_failed_photo') def download_by_photo_detail(self, photo: JmPhotoDetail, client: JmcomicClient): client.check_photo(photo) @@ -91,6 +116,7 @@ def download_by_photo_detail(self, photo: JmPhotoDetail, client: JmcomicClient): ) self.after_photo(photo) + @catch_exception('download_failed_image') def download_by_image_detail(self, image: JmImageDetail, client: JmcomicClient): img_save_path = self.option.decide_image_filepath(image) @@ -110,17 +136,11 @@ def download_by_image_detail(self, image: JmImageDetail, client: JmcomicClient): if use_cache is True and image.exists: return - try: - client.download_by_image_detail( - image, - img_save_path, - decode_image=decode_image, - ) - except BaseException as e: - jm_log('image.failed', f'图片下载失败: [{image.download_url}], 异常: {e}') - # 保存失败记录 - self.download_failed_list.append((image, e)) - raise + client.download_by_image_detail( + image, + img_save_path, + decode_image=decode_image, + ) self.after_image(image, img_save_path) @@ -189,7 +209,7 @@ def all_success(self) -> bool: 注意!如果使用了filter机制,例如通过filter只下载3张图片,那么all_success也会为False """ - if len(self.download_failed_list) != 0: + if len(self.download_failed_image) != 0: return False for album, photo_dict in self.download_success_dict.items(): @@ -259,6 +279,15 @@ def after_image(self, image: JmImageDetail, img_save_path): downloader=self, ) + def raise_if_have_exception(self): + if len(self.download_failed_image) == 0 and len(self.download_success_dict) == 0: + return + ExceptionTool.raises( + f'部分下载失败: 有{len(self.download_failed_photo)}个章节下载失败, {len(self.download_failed_image)}个图片下载失败', + {'downloader': self}, + PartialDownloadFailedException, + ) + # 下面是对with语法的支持 def __enter__(self): diff --git a/src/jmcomic/jm_exception.py b/src/jmcomic/jm_exception.py index a60402988..388f183fa 100644 --- a/src/jmcomic/jm_exception.py +++ b/src/jmcomic/jm_exception.py @@ -15,6 +15,7 @@ def from_context(self, key): def __str__(self): return self.msg + class ResponseUnexpectedException(JmcomicException): description = '响应不符合预期异常' @@ -44,7 +45,6 @@ def pattern(self): class JsonResolveFailException(ResponseUnexpectedException): description = 'Json解析异常' - pass class MissingAlbumPhotoException(ResponseUnexpectedException): @@ -57,7 +57,10 @@ def error_jmid(self) -> str: class RequestRetryAllFailException(JmcomicException): description = '请求重试全部失败异常' - pass + + +class PartialDownloadFailedException(JmcomicException): + description = '部分章节或图片下载失败异常' class ExceptionTool: From 129a866a1c0b3245ca2dc4c083c517d396c83f16 Mon Sep 17 00:00:00 2001 From: hect0x7 <93357912+hect0x7@users.noreply.github.com> Date: Tue, 8 Apr 2025 17:16:43 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E4=B8=8A=E6=8A=9B=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jmcomic/jm_downloader.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/jmcomic/jm_downloader.py b/src/jmcomic/jm_downloader.py index 15391f70d..062d27d1f 100644 --- a/src/jmcomic/jm_downloader.py +++ b/src/jmcomic/jm_downloader.py @@ -209,7 +209,7 @@ def all_success(self) -> bool: 注意!如果使用了filter机制,例如通过filter只下载3张图片,那么all_success也会为False """ - if len(self.download_failed_image) != 0: + if not self.is_empty_download_failed: return False for album, photo_dict in self.download_success_dict.items(): @@ -222,6 +222,10 @@ def all_success(self) -> bool: return True + @property + def is_empty_download_failed(self): + return len(self.download_failed_image) == 0 and len(self.download_failed_photo) == 0 + # 下面是回调方法 def before_album(self, album: JmAlbumDetail): @@ -280,7 +284,7 @@ def after_image(self, image: JmImageDetail, img_save_path): ) def raise_if_have_exception(self): - if len(self.download_failed_image) == 0 and len(self.download_success_dict) == 0: + if self.is_empty_download_failed: return ExceptionTool.raises( f'部分下载失败: 有{len(self.download_failed_photo)}个章节下载失败, {len(self.download_failed_image)}个图片下载失败', From 783bf4cff743f84ca35489e521661da4249c171d Mon Sep 17 00:00:00 2001 From: hect0x7 <93357912+hect0x7@users.noreply.github.com> Date: Tue, 8 Apr 2025 17:18:08 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E4=B8=8A=E6=8A=9B=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jmcomic/jm_downloader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jmcomic/jm_downloader.py b/src/jmcomic/jm_downloader.py index 062d27d1f..145825416 100644 --- a/src/jmcomic/jm_downloader.py +++ b/src/jmcomic/jm_downloader.py @@ -63,8 +63,8 @@ def wrapper(self, *args, **kwargs): try: return func(self, *args, **kwargs) except Exception as e: - getattr(self, field_name).append(e) detail: JmBaseEntity = args[1] + getattr(self, field_name).append((detail, e)) if detail.is_image(): detail: JmImageDetail jm_log('image.failed', f'图片下载失败: [{detail.download_url}], 异常: {e}') From 09471dd3279c2d3679fe7f6b3e45f5ca3217e642 Mon Sep 17 00:00:00 2001 From: hect0x7 <93357912+hect0x7@users.noreply.github.com> Date: Tue, 8 Apr 2025 17:20:22 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E4=B8=8A=E6=8A=9B=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jmcomic/jm_downloader.py | 46 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/jmcomic/jm_downloader.py b/src/jmcomic/jm_downloader.py index 145825416..59adbaaf2 100644 --- a/src/jmcomic/jm_downloader.py +++ b/src/jmcomic/jm_downloader.py @@ -1,6 +1,29 @@ from .jm_option import * +def catch_exception(field_name): + def deco(func): + def wrapper(self, *args, **kwargs): + try: + return func(self, *args, **kwargs) + except Exception as e: + detail: JmBaseEntity = args[1] + getattr(self, field_name).append((detail, e)) + if detail.is_image(): + detail: JmImageDetail + jm_log('image.failed', f'图片下载失败: [{detail.download_url}], 异常: {e}') + + elif detail.is_photo(): + detail: JmPhotoDetail + jm_log('photo.failed', f'章节下载失败: [{detail.id}], 异常: {e}') + + raise e + + return wrapper + + return deco + + # noinspection PyMethodMayBeStatic class DownloadCallback: @@ -56,29 +79,6 @@ def __init__(self, option: JmOption) -> None: self.download_failed_image: List[Tuple[JmImageDetail, BaseException]] = [] self.download_failed_photo: List[Tuple[JmPhotoDetail, BaseException]] = [] - @staticmethod - def catch_exception(field_name): - def deco(func): - def wrapper(self, *args, **kwargs): - try: - return func(self, *args, **kwargs) - except Exception as e: - detail: JmBaseEntity = args[1] - getattr(self, field_name).append((detail, e)) - if detail.is_image(): - detail: JmImageDetail - jm_log('image.failed', f'图片下载失败: [{detail.download_url}], 异常: {e}') - - elif detail.is_photo(): - detail: JmPhotoDetail - jm_log('photo.failed', f'章节下载失败: [{detail.id}], 异常: {e}') - - raise e - - return wrapper - - return deco - def download_album(self, album_id): client = self.client_for_album(album_id) album = client.get_album_detail(album_id) From b02994d79f5961371bca86c72a612254a1eecdd7 Mon Sep 17 00:00:00 2001 From: hect0x7 <93357912+hect0x7@users.noreply.github.com> Date: Tue, 8 Apr 2025 21:07:53 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E4=B8=8A=E6=8A=9B=E6=9C=BA=E5=88=B6=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jmcomic/jm_downloader.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/jmcomic/jm_downloader.py b/src/jmcomic/jm_downloader.py index 59adbaaf2..07f7d0c41 100644 --- a/src/jmcomic/jm_downloader.py +++ b/src/jmcomic/jm_downloader.py @@ -287,7 +287,9 @@ def raise_if_have_exception(self): if self.is_empty_download_failed: return ExceptionTool.raises( - f'部分下载失败: 有{len(self.download_failed_photo)}个章节下载失败, {len(self.download_failed_image)}个图片下载失败', + f'部分下载失败: 有{len(self.download_failed_photo)}个章节下载失败, {len(self.download_failed_image)}个图片下载失败。\n' + + f'失败章节IDs: {[photo.id for photo, _ in self.download_failed_photo][:5]}{"..." if len(self.download_failed_photo) > 5 else ""}\n' + + f'失败图片URLs: {[image.download_url for image, _ in self.download_failed_image][:5]}{"..." if len(self.download_failed_image) > 5 else ""}', {'downloader': self}, PartialDownloadFailedException, )