From c8f9e631ca536e2ede20356c8d25bbc8eb32bbe5 Mon Sep 17 00:00:00 2001 From: hect0x7 <93357912+hect0x7@users.noreply.github.com> Date: Wed, 7 Oct 2026 17:56:50 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix(client):=20=E4=BC=98=E5=8C=96=20API=20?= =?UTF-8?q?=E5=93=8D=E5=BA=94=20JSON=20=E8=A7=A3=E6=9E=90=E4=B8=8E?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=EF=BC=8C=E5=85=BC=E5=AE=B9=20UTF-8=20BOM=20?= =?UTF-8?q?=E5=93=8D=E5=BA=94=20(#585)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jmcomic/jm_async_client.py | 18 ++++++++---------- src/jmcomic/jm_client_impl.py | 22 ++++++++-------------- src/jmcomic/jm_toolkit.py | 2 +- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/jmcomic/jm_async_client.py b/src/jmcomic/jm_async_client.py index 5980c056..0567e883 100644 --- a/src/jmcomic/jm_async_client.py +++ b/src/jmcomic/jm_async_client.py @@ -337,16 +337,14 @@ def _raise_if_resp_should_retry(resp): # /chapter_view_template 这个接口不是返回json数据,不做检查 return - # 检查响应的第一个有效字符是否为 '{'(JSON 格式) - text = resp.text - for char in text: - if char not in (' ', '\n', '\t'): - ExceptionTool.require_true( - char == '{', - f'请求不是json格式,强制重试!响应文本: [{JmcomicText.limit_text(text, 200)}]' - ) - return - ExceptionTool.raises_resp(f'响应无数据!', resp) + try: + JmcomicText.try_parse_json_object(getattr(resp, 'text', '')) + except Exception: + text = getattr(resp, 'text', '') + ExceptionTool.raises_resp( + f'请求不是json格式,强制重试!响应文本: [{JmcomicText.limit_text(text, 200)}]', + resp, + ) @staticmethod def _require_resp_success(resp: JmApiResp): diff --git a/src/jmcomic/jm_client_impl.py b/src/jmcomic/jm_client_impl.py index da84f211..c124ddf6 100644 --- a/src/jmcomic/jm_client_impl.py +++ b/src/jmcomic/jm_client_impl.py @@ -1244,9 +1244,6 @@ def raise_if_resp_should_retry(self, resp, is_image): """ 该方法会判断resp返回值是否是json格式, 如果不是,大概率是禁漫内部异常,需要进行重试 - - 由于完整的json格式校验会有性能开销,所以只做简单的检查, - 只校验第一个有效字符是不是 '{',如果不是,就认为异常数据,需要重试 """ resp = super().raise_if_resp_should_retry(resp, is_image) @@ -1266,17 +1263,14 @@ def raise_if_resp_should_retry(self, resp, is_image): # /chapter_view_template 这个接口不是返回json数据,不做检查 return resp - text = resp.text - for char in text: - if char not in (' ', '\n', '\t'): - # 找到第一个有效字符 - ExceptionTool.require_true( - char == '{', - f'请求不是json格式,强制重试!响应文本: [{JmcomicText.limit_text(text, 200)}]' - ) - return resp - - ExceptionTool.raises_resp(f'响应无数据!request_url=[{url}]', resp) + try: + JmcomicText.try_parse_json_object(resp.text) + return resp + except Exception: + ExceptionTool.raises_resp( + f'请求不是json格式,强制重试!响应文本: [{JmcomicText.limit_text(resp.text, 200)}]', + resp, + ) def after_init(self): # 自动更新禁漫API域名 diff --git a/src/jmcomic/jm_toolkit.py b/src/jmcomic/jm_toolkit.py index 5c95fed4..bd0411ad 100644 --- a/src/jmcomic/jm_toolkit.py +++ b/src/jmcomic/jm_toolkit.py @@ -408,7 +408,7 @@ def try_mkdir(cls, save_dir: str): @classmethod def try_parse_json_object(cls, resp_text: str) -> dict: import json - text = resp_text.strip() + text = resp_text.strip().lstrip('\ufeff') if text.startswith('{') and text.endswith('}'): # fast case return json.loads(text) From 921cf4daca081b87138071a97c95ae3be1cdc7d8 Mon Sep 17 00:00:00 2001 From: hect0x7 <93357912+hect0x7@users.noreply.github.com> Date: Wed, 7 Oct 2026 18:03:42 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix(workflow):=20=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E7=A9=BA=E4=B8=8B=E8=BD=BD=E5=9C=BA=E6=99=AF=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E4=B8=8B=E8=BD=BD=E7=9B=AE=E5=BD=95=E4=B8=8D=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E5=AF=BC=E8=87=B4=E5=8E=8B=E7=BC=A9=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/download.yml | 8 ++++++++ .github/workflows/download_dispatch.yml | 10 +++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/download.yml b/.github/workflows/download.yml index 5f9e3ab9..31bca7ce 100644 --- a/.github/workflows/download.yml +++ b/.github/workflows/download.yml @@ -72,12 +72,20 @@ jobs: python workflow_download.py - name: 压缩文件 + id: compress run: | + if [ ! -d "$JM_DOWNLOAD_DIR" ] || [ -z "$(ls -A "$JM_DOWNLOAD_DIR")" ]; then + echo "has_files=false" >> $GITHUB_OUTPUT + echo "下载目录不存在或为空,跳过压缩" + exit 0 + fi cd $JM_DOWNLOAD_DIR tar -zcvf "../$ZIP_NAME" ./ mv "../$ZIP_NAME" . + echo "has_files=true" >> $GITHUB_OUTPUT - name: 上传结果 + if: steps.compress.outputs.has_files == 'true' uses: actions/upload-artifact@v4 with: name: ${{ env.UPLOAD_NAME }} diff --git a/.github/workflows/download_dispatch.yml b/.github/workflows/download_dispatch.yml index fa111841..2c82f799 100644 --- a/.github/workflows/download_dispatch.yml +++ b/.github/workflows/download_dispatch.yml @@ -156,15 +156,23 @@ jobs: python workflow_download.py - name: 压缩文件 + id: compress run: | + if [ ! -d "$JM_DOWNLOAD_DIR" ] || [ -z "$(ls -A "$JM_DOWNLOAD_DIR")" ]; then + echo "has_files=false" >> $GITHUB_OUTPUT + echo "下载目录不存在或为空,跳过压缩" + exit 0 + fi cd $JM_DOWNLOAD_DIR tar -zcvf "../$ZIP_NAME" ./ mv "../$ZIP_NAME" . + echo "has_files=true" >> $GITHUB_OUTPUT - name: 上传结果 + if: steps.compress.outputs.has_files == 'true' uses: actions/upload-artifact@v4 with: name: ${{ env.UPLOAD_NAME }} path: ${{ env.JM_DOWNLOAD_DIR }}/${{ env.ZIP_NAME }} - if-no-files-found: error + if-no-files-found: warn retention-days: 90 From cb9a5076ad7a942dc43a111533751c027ade76bb Mon Sep 17 00:00:00 2001 From: hect0x7 <93357912+hect0x7@users.noreply.github.com> Date: Wed, 7 Oct 2026 18:26:23 +0800 Subject: [PATCH 3/3] =?UTF-8?q?v2.7.8:=20=E4=BF=AE=E5=A4=8D=20API=20?= =?UTF-8?q?=E5=93=8D=E5=BA=94=20UTF-8=20BOM=20=E8=A7=A3=E6=9E=90=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E9=87=8D=E8=AF=95=E5=A4=B1=E8=B4=A5=E9=97=AE=E9=A2=98?= =?UTF-8?q?=20(#585)=EF=BC=8C=E5=85=BC=E5=AE=B9=E7=A9=BA=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 10 ++++++++++ src/jmcomic/__init__.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5549b535..1b480db2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ 条目分类参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/), 版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。 +## [2.7.8] - 2026-10-07 + +### Summary + +本次更新修复禁漫 API 响应携带 UTF-8 BOM 导致 JSON 解析与校验失败的问题(#585),并兼容空下载场景下的 GitHub Actions 工作流。 + +### Fixed + +- **API 响应 UTF-8 BOM 兼容与校验统一**:在 `JmcomicText.try_parse_json_object` 中增加对 UTF-8 BOM 字符(`\ufeff`)的过滤剥离,并在同步/异步客户端中统一使用该方法进行 API 响应合法性校验,彻底解决因 BOM 导致客户端误判为非 JSON 响应并反复重试耗尽的问题([#585](https://github.com/hect0x7/JMComic-Crawler-Python/issues/585))。 +- **下载 Actions 工作流空目录保护**:在 `download.yml` 与 `download_dispatch.yml` 中增加对 `$JM_DOWNLOAD_DIR` 是否存在及是否非空的判断;在无本子下载时跳过压缩和构件上传,避免目录不存在导致工作流异常中断。 ## [2.7.7] - 2026-09-12 diff --git a/src/jmcomic/__init__.py b/src/jmcomic/__init__.py index 4745c5ca..21515759 100644 --- a/src/jmcomic/__init__.py +++ b/src/jmcomic/__init__.py @@ -2,7 +2,7 @@ # 被依赖方 <--- 使用方 # config <--- entity <--- toolkit <--- client <--- option <--- downloader -__version__ = '2.7.7' +__version__ = '2.7.8' from .jm_task_context import * from .api import *