Skip to content

refactor(resource): 统一资源管理、下载队列与多源回退 - #2638

Open
ShadowLemoon wants to merge 7 commits into
mainfrom
refactor/resource-download
Open

refactor(resource): 统一资源管理、下载队列与多源回退#2638
ShadowLemoon wants to merge 7 commits into
mainfrom
refactor/resource-download

Conversation

@ShadowLemoon

@ShadowLemoon ShadowLemoon commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator
  • feat(resource): 增加项目级多源下载能力
  • refactor(resource): 由框架加载项目模型声明
  • feat(resource): 加入统一下载队列和更新聚合
  • refactor(ui): 整合资源管理与下载队列入口
  • docs(resource): 补充资源下载与队列设计

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

新增(Added)

  • 新增统一资源管理中心,管理启动器、OCR 和项目模型的检查、下载及版本切换。
  • 新增多源下载配置,支持 GitHub、CNB 和 Gitee,以及语言推荐、代理和候选源回退。
  • 新增统一下载队列,支持任务去重、优先级、取消、重试、进度展示和状态管理。
  • 新增资源更新协调器和统一更新弹窗,聚合启动器、OCR 和模型更新。
  • 新增下载确认事件,支持记住选择和免确认下载。
  • 新增开发者模式及代码同步、版本记录和回滚入口。
  • 新增启动器更新服务,支持暂存、替换、备份、回滚和中断恢复。
  • 新增资源下载与队列设计文档。

变更(Changed)

  • 将项目模型声明改为由框架通过 RepoConfig 加载。
  • 将 OCR、YOLO 和启动器下载流程改为使用统一资源源顺序、成功或失败回调及慢速回退机制。
  • 将 HTTP 下载改为报告结构化进度,并支持低速检测和自动切换资源源。
  • 将资源管理界面与原代码同步及版本管理功能合并。
  • 在标题栏新增下载队列入口和活动、失败任务数量提示。

移除(Removed)

  • 移除独立的代码同步界面。
  • 移除独立的资源下载设置页面。
  • 移除分散的 GitHub、Gitee 模型下载地址生成逻辑。
  • 移除旧版按下载源布尔开关控制的下载接口。

Walkthrough

Changes

统一资源下载与更新体系

Layer / File(s) Summary
资源配置与模型契约
config/repository.yml, src/one_dragon/envs/repo_config.py, src/one_dragon/envs/env_config.py, src/one_dragon/base/config/basic_model_config.py
新增资源源、仓库和模型配置。新增候选源排序、模型完整性检查和下载参数构造。
多源下载与状态回调
src/one_dragon/utils/http_utils.py, src/one_dragon/base/web/*, src/one_dragon/base/matcher/ocr/*
下载器支持多源顺序、代理、低速回退、取消、进度状态和源成功或失败回调。
更新服务与后台下载确认
src/one_dragon/envs/update_service.py, src/one_dragon/base/operation/*
新增启动器更新、暂存替换、回滚恢复和 OCR 缺失模型确认流程。
下载队列与资源更新编排
src/one_dragon_qt/services/*
新增串行下载队列和资源更新协调器,统一处理启动器、OCR 和模型资源。
资源管理与下载界面
src/one_dragon_qt/view/resource_management_interface.py, src/one_dragon_qt/widgets/*
新增资源管理页、更新弹窗、下载队列弹窗和队列化下载卡片。
窗口集成与模型调用方迁移
src/one_dragon_qt/windows/*, src/zzz_od/gui/*, src/zzz_od/yolo/*, src/zzz_od/application/*
窗口和应用导航接入资源更新流程。模型调用方改为接收配置生成的下载地址。

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确概括了资源管理、下载队列和多源回退的主要变更,内容清晰且具体。
Description check ✅ Passed 描述列出了项目级多源下载、模型声明、下载队列、更新聚合、界面整合和文档补充,与变更内容相关。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch refactor/resource-download
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/resource-download

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/zzz_od/application/hollow_zero/lost_void/context/lost_void_detector.py (1)

21-37: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

为新增参数补充文档说明。

__init__ 新增了必填参数 model_download_url,但文档字符串(Line 31-37)没有对应的 :param 说明。补充说明,保持文档与函数签名一致。

📝 建议的修改
         """
         崩铁用的YOLO模型 参考自 https://github.com/ibaiGorordo/ONNX-YOLOv8-Object-Detection
         :param model_name: 模型名称 在根目录下会有一个以模型名称创建的子文件夹
         :param backup_model_name: 放置所有模型的根目录
+        :param model_download_url: 模型下载根地址
         :param gpu: 是否启用GPU运算
         :param keep_result_seconds: 保留多长时间的识别结果
         """

As per path instructions, "注释应使用Google风格,函数职责较重时必须有注释".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/zzz_od/application/hollow_zero/lost_void/context/lost_void_detector.py`
around lines 21 - 37, 在 __init__ 的文档字符串中补充 model_download_url
参数的说明,明确其用途,并保持现有参数文档风格与函数签名一致。

Source: Path instructions

src/one_dragon_qt/windows/main_app_window_base.py (1)

63-99: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

为新增成员变量补充类型注解。

self.download_queue(Line 64)和 self.download_queue_dialog(Line 75)缺少类型注解。同一个 __init__ 方法里,self._download_queue_tip(Line 76)和 self.resource_update_coordinator(Line 77-85)都带有类型注解。为这两个变量补充类型注解,保持风格一致。

🔧 建议的修改
-        self.download_queue = DownloadQueueService(ctx)
+        self.download_queue: DownloadQueueService = DownloadQueueService(ctx)
-        self.download_queue_dialog = DownloadQueueDialog(self.download_queue, self)
+        self.download_queue_dialog: DownloadQueueDialog = DownloadQueueDialog(self.download_queue, self)

As per coding guidelines, "src/**/*.py: 项目使用 Python 3.11;所有函数签名和类成员变量都必须有类型注解".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/windows/main_app_window_base.py` around lines 63 - 99, 为
__init__ 方法中的 self.download_queue 和 self.download_queue_dialog 补充类型注解,分别使用
DownloadQueueService 和 DownloadQueueDialog,与现有 self._download_queue_tip 及
self.resource_update_coordinator 的成员变量注解风格保持一致。

Source: Coding guidelines

🧹 Nitpick comments (9)
src/one_dragon_qt/view/resource_management_interface.py (2)

444-470: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

建议为启动器类型提供公开访问方式。

第 446 行读取 self.launcher_opt._launcher_type,跨类访问私有属性。LauncherDownloadCard 内部若重命名该属性,此处会静默失效。建议在 LauncherDownloadCard 上增加只读属性 launcher_type,此处改用公开属性。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/view/resource_management_interface.py` around lines 444 -
470, 在 LauncherDownloadCard 中增加只读公开属性 launcher_type,用于返回内部的启动器类型;更新
_build_launcher_spec,改为读取 self.launcher_opt.launcher_type,避免直接访问私有属性
_launcher_type。

61-83: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

避免用 finished 命名自定义线程信号。

FetchTotalRunnerFetchPageRunner 覆盖 QThread.finished 后,PySide6 会把该名称解析为子类自定义信号,导致线程生命周期结束的 QThread.finished 无法被外部直接连接。如果当前连接都服务于结果回调,将信号改为 result_ready,并更新两处 finished.connect() 调用;如果后续需要监听线程结束事件,避免覆盖同名信号或直接连接父类信号。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/view/resource_management_interface.py` around lines 61 -
83, Rename the custom finished signals in FetchTotalRunner and FetchPageRunner
to result_ready, update their emit calls and both corresponding
finished.connect() usages, and preserve QThread.finished for thread lifecycle
notifications.
src/one_dragon_qt/widgets/setting_card/common_download_card.py (2)

88-91: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

建议消除隐式 Optional 注解。

extra_btn_list: list[QAbstractButton] = None 使用了 PEP 484 禁止的隐式 Optional。Ruff 报出 RUF013。项目规范要求使用 X | Y 现代联合语法。

♻️ 建议修改
-            extra_btn_list: list[QAbstractButton] = None,
+            extra_btn_list: list[QAbstractButton] | None = None,

依据编码规范:“所有函数签名和类成员变量都必须有类型注解,并优先使用 list[str]X | Y 等现代语法。”

Also applies to: 388-391

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/widgets/setting_card/common_download_card.py` around lines
88 - 91, Update the extra_btn_list annotations in the affected function
signatures to explicitly allow None using modern union syntax, such as
list[QAbstractButton] | None, while preserving the existing defaults and
behavior.

Sources: Coding guidelines, Linters/SAST tools


152-155: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

建议为 lambda 连接指定接收者,避免卡片销毁后的悬垂调用。

第 154 行连接的是 lambda。Qt 只在接收者是 QObject 的绑定方法时自动断开连接。lambda 没有关联接收者,卡片销毁后服务信号仍会调用它,可能触发 RuntimeError: Internal C++ object already deleted。建议改为绑定方法或传入 self 作为连接上下文。

♻️ 建议方案
-        service.task_removed.connect(lambda _task_key: self.check_and_update_display())
+        service.task_removed.connect(self._on_queue_task_removed)

并新增方法:

    def _on_queue_task_removed(self, _task_key: str) -> None:
        """队列任务被移除时刷新卡片。"""
        self.check_and_update_display()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/widgets/setting_card/common_download_card.py` around lines
152 - 155, Replace the lambda connected to service.task_removed with a bound
method on the card, adding _on_queue_task_removed(self, _task_key: str) to call
check_and_update_display(). Keep the existing task-added and task-updated
connections unchanged so the signal is associated with the card QObject and is
disconnected safely on destruction.
src/one_dragon_qt/widgets/download_card/launcher_download_card.py (1)

94-98: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

建议与父类保持参数名一致。

父类方法签名为 _get_downloader_param(self, index: int | None = None),此处覆写为 _idx。如果后续有调用方使用 index= 关键字参数,覆写方法会抛 TypeError。建议改名为 index

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/widgets/download_card/launcher_download_card.py` around
lines 94 - 98, 将 LauncherDownloadCard 的 _get_downloader_param 方法参数从 _idx 重命名为
index,使覆写签名与父类一致并支持使用 index= 关键字调用;保持现有参数传递和返回逻辑不变。
src/one_dragon_qt/view/source_config_interface.py (1)

20-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

两个视图文件都从弹窗模块导入 build_resource_source_options,形成视图对弹窗的依赖。 共同根因是这个纯配置转换函数被放在了 widgets/resource_download_dialog.py 中。建议把它移动到 one_dragon/envs/repo_config.py 或独立工具模块。

  • src/one_dragon_qt/view/source_config_interface.py#L20-L22:改为从新位置导入。
  • src/one_dragon_qt/view/resource_management_interface.py#L44-L46:改为从新位置导入。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/view/source_config_interface.py` around lines 20 - 22,
将纯配置转换函数 build_resource_source_options 从弹窗模块移动到 one_dragon/envs/repo_config.py
或独立工具模块,并更新 src/one_dragon_qt/view/source_config_interface.py 的导入;同时更新
src/one_dragon_qt/view/resource_management_interface.py 的对应导入,使两个视图不再依赖
resource_download_dialog.py。
src/zzz_od/yolo/flash_classifier.py (1)

19-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

两个 YOLO 封装类新增了必填参数 model_download_url,但 docstring 未同步。 共同根因是模型下载地址来源改为由调用方注入后,构造函数文档没有随之更新。

  • src/zzz_od/yolo/flash_classifier.py#L19-L24:补充 :param model_download_url: 说明。
  • src/zzz_od/yolo/hollow_event_detector.py#L17-L23:补充 :param model_download_url: 说明,并删除已不存在的 model_parent_dir_path 参数说明。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/zzz_od/yolo/flash_classifier.py` around lines 19 - 24, Update the
constructor docstrings for the YOLO wrapper classes: in
src/zzz_od/yolo/flash_classifier.py lines 19-24, document the required
model_download_url parameter; in src/zzz_od/yolo/hollow_event_detector.py lines
17-23, document model_download_url and remove the obsolete model_parent_dir_path
entry.
src/one_dragon_qt/widgets/resource_download_dialog.py (1)

76-76: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

改用 checkStateChanged 以兼容当前 PySide6 签名规范。

PySide6 依赖锁定在 6.8.0.2,Qt 6.8 已将 QCheckBox.stateChanged 标记为弃用;同项目其他复选框已使用 checkStateChanged。将此处连接改为 checkStateChanged,并让 _update_confirm_enabled 接收 PySide6.QtCore.Qt.CheckState 值。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/widgets/resource_download_dialog.py` at line 76,
在资源下载对话框的复选框连接处,将 check.stateChanged 改为 check.checkStateChanged,以符合 PySide6 6.8
的签名规范;同时更新 _update_confirm_enabled,使其接收并正确处理 PySide6.QtCore.Qt.CheckState
值,保持确认按钮启用状态逻辑不变。
src/one_dragon_qt/windows/window.py (1)

223-232: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

移除未使用的 downloadQueueButton 动态属性。

downloadQueueButton 的样式在 title_bar.qss 中仅通过对象名、状态和 homeMode 选择器定义,没有 hasError 相关选择器;failed > 0 的颜色变化已由 setStyleSheet 直接控制。setProperty("hasError", ...) 不会影响当前样式效果,可以移除。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/windows/window.py` around lines 223 - 232, Remove the
unused setProperty("hasError", ...) call from set_download_queue_counts; retain
the existing text updates and direct setStyleSheet color handling based on
failed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/one_dragon_qt/services/download_queue_service.py`:
- Around line 324-345: Update the progress-signal wiring in the worker-start
flow and _on_progress_changed so each callback carries and updates the task that
emitted it, rather than reading self._current_task at delivery time. Bind the
task object when connecting DownloadTaskWorker.progress_changed, preserve the
existing progress/state transitions, and emit task_updated for that bound task
even if _current_task has already advanced.

In `@src/one_dragon_qt/services/resource_update_coordinator.py`:
- Around line 325-336: Update the OCR request flow around `_on_task_updated` to
also listen for `queue.task_removed`, so removal of the matching task completes
the request immediately instead of waiting for the timeout. Reuse the existing
task-key matching, callback disconnection, and `_respond_ocr_request` logic,
returning a failure response for removed tasks while preserving terminal-state
success handling.

In `@src/one_dragon_qt/view/setting/setting_env_interface.py`:
- Around line 188-192: 在设置界面的初始化逻辑及相关可见性更新逻辑中,移除对 developer_mode_opt 自身的
setVisible 绑定,确保关闭开发者模式后该开关仍保持可见;保留 developer_mode 对
debug_opt、_on_developer_mode_changed() 和标题栏状态的刷新行为。

In `@src/one_dragon_qt/view/setting/setting_instance_interface.py`:
- Around line 231-236: 在 `_refresh_content_widget` 重建内容控件后立即调用
`_update_developer_visibility`,确保新建的 `custom_win_title_opt` 遵循当前开发者模式。移除
`_update_developer_visibility` 中多余的 `hasattr` 判断,直接更新已在 `get_content_widget`
阶段创建的控件可见性。

In `@src/one_dragon_qt/widgets/download_card/launcher_download_card.py`:
- Around line 35-39: Update the run method to catch exceptions from
update_service.get_launcher_version_info, log the failure using the imported
one_dragon.utils.log_utils.log, and emit check_finished with safe fallback
version values in the exception path so the UI exits its checking state and
remains usable.

In `@src/one_dragon_qt/widgets/setting_card/common_download_card.py`:
- Around line 173-176: Update the ResourceDownloadTaskState.WAITING branch in
the download card state handling to display “等待中” instead of “下载中”, matching
DownloadQueueDialog while preserving the existing enabled-button behavior and
return value.
- Around line 157-166: 缓存由 download_spec_factory 生成的 task_key,避免 _get_queue_task
在高频 _on_queue_task_changed 刷新中重复构造规格;在 __init__ 初始化 _cached_task_key,并在
on_index_changed 及下载类型切换时将其重置为 None,确保选项变化后重新生成规格,其他情况下复用缓存键查询队列任务。

In `@src/one_dragon/base/config/basic_model_config.py`:
- Around line 62-76: 更新
BasicModelConfig.get_model_download_base_url,移除默认固定为“github”的行为;当调用方未提供
source_id 时,按 EnvConfig.get_resource_source_order() 的候选顺序解析并返回第一个可用下载地址,同时保留显式
source_id 的现有查询与未配置时的 ValueError 行为。

In `@src/one_dragon/base/matcher/ocr/ocr_matcher.py`:
- Around line 31-39: Update the base OcrMatcher.init_model signature to accept
on_source_failure and fallback_on_slow, matching the keyword arguments passed by
one_dragon_context.init_ocr. Keep the existing parameter types and defaults
consistent with the concrete OnnxOcrMatcher implementation so injected
OcrMatcher subclasses conform to the shared interface.

In `@src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py`:
- Around line 22-40: 更新 get_ocr_download_urls,优先通过
RepoConfig.get_resource_asset_urls 获取 env 仓库对应模型压缩包的下载地址,使用与
update_service.get_launcher_download_param 一致的配置优先策略;仅在 RepoConfig 缺少相关配置时回退到
OCR_DOWNLOAD_URLS,保留现有返回结构并避免新增硬编码源列表。

In `@src/one_dragon/base/operation/one_dragon_context.py`:
- Around line 508-512: Update OnnxOcrMatcher.ocr() to lazily call init_model()
before accessing or using self._model, ensuring recognition remains safe when
init_ocr() skips model initialization after download cancellation. Preserve
existing behavior for already-loaded models and cover every public recognition
path through ocr().
- Around line 518-525: 同步 OcrMatcher.init_model 的接口与调用参数:在基类 init_model 签名中补充
on_source_failure 和 fallback_on_slow,并确保其他实现(包括 OnnxOcrMatcher)兼容并正确处理这两个参数,避免
self.ocr.init_model 调用触发 TypeError。

In `@src/one_dragon/envs/update_service.py`:
- Around line 176-185: Update _swap_launcher_backup so exceptions are re-raised
when backup=True, while retaining log-only handling for rollback operations;
ensure prepare_launcher_update propagates backup failures and
apply_staged_launcher_update does not replace the launcher unless backups
complete successfully.

In `@src/one_dragon/utils/http_utils.py`:
- Around line 143-144: 调整 http 下载流程中 opener.open 的 timeout 配置,避免 5
秒读超时使弱网或最后候选源在短暂无数据时失败;将读超时放宽至 15–30 秒,并继续由 min_bytes_per_second
负责低速下载淘汰,同时保持连接超时控制。

In `@src/zzz_od/gui/app.py`:
- Around line 256-260: 为 closeEvent 方法的 event 参数添加 QCloseEvent 类型注解,并将返回类型标注为
None;确认从 PySide6.QtGui 导入 QCloseEvent,保持现有关闭事件处理逻辑不变。

---

Outside diff comments:
In `@src/one_dragon_qt/windows/main_app_window_base.py`:
- Around line 63-99: 为 __init__ 方法中的 self.download_queue 和
self.download_queue_dialog 补充类型注解,分别使用 DownloadQueueService 和
DownloadQueueDialog,与现有 self._download_queue_tip 及
self.resource_update_coordinator 的成员变量注解风格保持一致。

In `@src/zzz_od/application/hollow_zero/lost_void/context/lost_void_detector.py`:
- Around line 21-37: 在 __init__ 的文档字符串中补充 model_download_url
参数的说明,明确其用途,并保持现有参数文档风格与函数签名一致。

---

Nitpick comments:
In `@src/one_dragon_qt/view/resource_management_interface.py`:
- Around line 444-470: 在 LauncherDownloadCard 中增加只读公开属性
launcher_type,用于返回内部的启动器类型;更新 _build_launcher_spec,改为读取
self.launcher_opt.launcher_type,避免直接访问私有属性 _launcher_type。
- Around line 61-83: Rename the custom finished signals in FetchTotalRunner and
FetchPageRunner to result_ready, update their emit calls and both corresponding
finished.connect() usages, and preserve QThread.finished for thread lifecycle
notifications.

In `@src/one_dragon_qt/view/source_config_interface.py`:
- Around line 20-22: 将纯配置转换函数 build_resource_source_options 从弹窗模块移动到
one_dragon/envs/repo_config.py 或独立工具模块,并更新
src/one_dragon_qt/view/source_config_interface.py 的导入;同时更新
src/one_dragon_qt/view/resource_management_interface.py 的对应导入,使两个视图不再依赖
resource_download_dialog.py。

In `@src/one_dragon_qt/widgets/download_card/launcher_download_card.py`:
- Around line 94-98: 将 LauncherDownloadCard 的 _get_downloader_param 方法参数从 _idx
重命名为 index,使覆写签名与父类一致并支持使用 index= 关键字调用;保持现有参数传递和返回逻辑不变。

In `@src/one_dragon_qt/widgets/resource_download_dialog.py`:
- Line 76: 在资源下载对话框的复选框连接处,将 check.stateChanged 改为 check.checkStateChanged,以符合
PySide6 6.8 的签名规范;同时更新 _update_confirm_enabled,使其接收并正确处理
PySide6.QtCore.Qt.CheckState 值,保持确认按钮启用状态逻辑不变。

In `@src/one_dragon_qt/widgets/setting_card/common_download_card.py`:
- Around line 88-91: Update the extra_btn_list annotations in the affected
function signatures to explicitly allow None using modern union syntax, such as
list[QAbstractButton] | None, while preserving the existing defaults and
behavior.
- Around line 152-155: Replace the lambda connected to service.task_removed with
a bound method on the card, adding _on_queue_task_removed(self, _task_key: str)
to call check_and_update_display(). Keep the existing task-added and
task-updated connections unchanged so the signal is associated with the card
QObject and is disconnected safely on destruction.

In `@src/one_dragon_qt/windows/window.py`:
- Around line 223-232: Remove the unused setProperty("hasError", ...) call from
set_download_queue_counts; retain the existing text updates and direct
setStyleSheet color handling based on failed.

In `@src/zzz_od/yolo/flash_classifier.py`:
- Around line 19-24: Update the constructor docstrings for the YOLO wrapper
classes: in src/zzz_od/yolo/flash_classifier.py lines 19-24, document the
required model_download_url parameter; in
src/zzz_od/yolo/hollow_event_detector.py lines 17-23, document
model_download_url and remove the obsolete model_parent_dir_path entry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1321c745-2f93-4baf-bc6b-9e31957afe7d

📥 Commits

Reviewing files that changed from the base of the PR and between d6717bc and 028807c.

📒 Files selected for processing (49)
  • config/repository.yml
  • deploy/module_manifest.py
  • docs/develop/README.md
  • docs/develop/one_dragon/modules/resource_download.md
  • src/one_dragon/base/config/basic_model_config.py
  • src/one_dragon/base/matcher/ocr/ocr_matcher.py
  • src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
  • src/one_dragon/base/operation/context_download_request_event.py
  • src/one_dragon/base/operation/context_event_bus.py
  • src/one_dragon/base/operation/one_dragon_context.py
  • src/one_dragon/base/operation/one_dragon_env_context.py
  • src/one_dragon/base/web/common_downloader.py
  • src/one_dragon/base/web/zip_downloader.py
  • src/one_dragon/envs/env_config.py
  • src/one_dragon/envs/repo_config.py
  • src/one_dragon/envs/update_service.py
  • src/one_dragon/utils/http_utils.py
  • src/one_dragon/yolo/yolo_utils.py
  • src/one_dragon_qt/_rc/qss/dark/title_bar.qss
  • src/one_dragon_qt/_rc/qss/light/title_bar.qss
  • src/one_dragon_qt/_rc/resource.py
  • src/one_dragon_qt/services/download_queue_service.py
  • src/one_dragon_qt/services/resource_update_coordinator.py
  • src/one_dragon_qt/view/code_interface.py
  • src/one_dragon_qt/view/resource_management_interface.py
  • src/one_dragon_qt/view/setting/resource_download_interface.py
  • src/one_dragon_qt/view/setting/setting_env_interface.py
  • src/one_dragon_qt/view/setting/setting_instance_interface.py
  • src/one_dragon_qt/view/source_config_interface.py
  • src/one_dragon_qt/widgets/download_card/launcher_download_card.py
  • src/one_dragon_qt/widgets/download_queue_dialog.py
  • src/one_dragon_qt/widgets/install_card/launcher_install_card.py
  • src/one_dragon_qt/widgets/resource_download_dialog.py
  • src/one_dragon_qt/widgets/setting_card/common_download_card.py
  • src/one_dragon_qt/widgets/teaching_tip.py
  • src/one_dragon_qt/windows/main_app_window_base.py
  • src/one_dragon_qt/windows/window.py
  • src/zzz_od/application/hollow_zero/lost_void/context/lost_void_context.py
  • src/zzz_od/application/hollow_zero/lost_void/context/lost_void_detector.py
  • src/zzz_od/auto_battle/auto_battle_dodge_context.py
  • src/zzz_od/config/model_config.py
  • src/zzz_od/context/zzz_context.py
  • src/zzz_od/gui/app.py
  • src/zzz_od/gui/view/home/home_interface.py
  • src/zzz_od/gui/view/setting/app_setting_interface.py
  • src/zzz_od/gui/view/setting/zzz_resource_download_interface.py
  • src/zzz_od/hollow_zero/hollow_map/hollow_zero_map_service.py
  • src/zzz_od/yolo/flash_classifier.py
  • src/zzz_od/yolo/hollow_event_detector.py
💤 Files with no reviewable changes (6)
  • src/one_dragon/yolo/yolo_utils.py
  • src/one_dragon_qt/view/setting/resource_download_interface.py
  • src/one_dragon_qt/view/code_interface.py
  • src/zzz_od/gui/view/setting/app_setting_interface.py
  • src/zzz_od/gui/view/setting/zzz_resource_download_interface.py
  • src/zzz_od/config/model_config.py

Comment on lines +324 to +345
worker = DownloadTaskWorker(self.ctx, task)
worker.progress_changed.connect(self._on_progress_changed)
worker.task_finished.connect(self._store_worker_result)
worker.finished.connect(self._on_worker_finished)
worker.finished.connect(worker.deleteLater)
self._worker = worker
self._worker_result = None
worker.start()

def _on_progress_changed(self, progress: ResourceDownloadProgress) -> None:
"""保存当前任务进度。"""
task = self._current_task
if task is None:
return
task.progress = progress
if progress.phase == 'extracting':
task.state = ResourceDownloadTaskState.EXTRACTING
elif progress.phase == 'applying':
task.state = ResourceDownloadTaskState.APPLYING
elif task.state != ResourceDownloadTaskState.CANCELLING:
task.state = ResourceDownloadTaskState.DOWNLOADING
self.task_updated.emit(task)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

进度回调应绑定发出该进度的任务。

_on_progress_changed 使用 self._current_task,不使用信号来源任务。_on_worker_finished 在第 384 行立即调用 _start_next(),把 _current_task 切换为下一个任务。如果上一个 worker 仍有排队中的 progress_changed 事件,处理时会写入新任务的 progress,并把新任务状态改为 DOWNLOADING。下载器在多源回退时可能从其他线程调用 status_callback,此时事件顺序不受保证。

建议在连接时绑定任务对象。

🛠️ 建议修改
         worker = DownloadTaskWorker(self.ctx, task)
-        worker.progress_changed.connect(self._on_progress_changed)
+        worker.progress_changed.connect(
+            lambda progress, bound_task=task: self._on_progress_changed(bound_task, progress)
+        )
         worker.task_finished.connect(self._store_worker_result)
-    def _on_progress_changed(self, progress: ResourceDownloadProgress) -> None:
-        """保存当前任务进度。"""
-        task = self._current_task
-        if task is None:
+    def _on_progress_changed(
+        self,
+        task: ResourceDownloadTask,
+        progress: ResourceDownloadProgress,
+    ) -> None:
+        """保存指定任务的进度。"""
+        if task is not self._current_task:
             return
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
worker = DownloadTaskWorker(self.ctx, task)
worker.progress_changed.connect(self._on_progress_changed)
worker.task_finished.connect(self._store_worker_result)
worker.finished.connect(self._on_worker_finished)
worker.finished.connect(worker.deleteLater)
self._worker = worker
self._worker_result = None
worker.start()
def _on_progress_changed(self, progress: ResourceDownloadProgress) -> None:
"""保存当前任务进度。"""
task = self._current_task
if task is None:
return
task.progress = progress
if progress.phase == 'extracting':
task.state = ResourceDownloadTaskState.EXTRACTING
elif progress.phase == 'applying':
task.state = ResourceDownloadTaskState.APPLYING
elif task.state != ResourceDownloadTaskState.CANCELLING:
task.state = ResourceDownloadTaskState.DOWNLOADING
self.task_updated.emit(task)
worker = DownloadTaskWorker(self.ctx, task)
worker.progress_changed.connect(
lambda progress, bound_task=task: self._on_progress_changed(bound_task, progress)
)
worker.task_finished.connect(self._store_worker_result)
worker.finished.connect(self._on_worker_finished)
worker.finished.connect(worker.deleteLater)
self._worker = worker
self._worker_result = None
worker.start()
def _on_progress_changed(
self,
task: ResourceDownloadTask,
progress: ResourceDownloadProgress,
) -> None:
"""保存指定任务的进度。"""
if task is not self._current_task:
return
task.progress = progress
if progress.phase == 'extracting':
task.state = ResourceDownloadTaskState.EXTRACTING
elif progress.phase == 'applying':
task.state = ResourceDownloadTaskState.APPLYING
elif task.state != ResourceDownloadTaskState.CANCELLING:
task.state = ResourceDownloadTaskState.DOWNLOADING
self.task_updated.emit(task)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/services/download_queue_service.py` around lines 324 - 345,
Update the progress-signal wiring in the worker-start flow and
_on_progress_changed so each callback carries and updates the task that emitted
it, rather than reading self._current_task at delivery time. Bind the task
object when connecting DownloadTaskWorker.progress_changed, preserve the
existing progress/state transitions, and emit task_updated for that bound task
even if _current_task has already advanced.

Comment on lines +325 to +336
def _on_task_updated(updated: ResourceDownloadTask) -> None:
if updated.task_key != task.task_key or updated.state not in terminal_states:
return
with contextlib.suppress(RuntimeError):
self.queue.task_updated.disconnect(_on_task_updated)
self._respond_ocr_request(
request,
updated.state == ResourceDownloadTaskState.SUCCEEDED,
remember,
)

self.queue.task_updated.connect(_on_task_updated)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

任务被移出队列时 OCR 请求不会得到应答。

_on_task_updated 只监听 task_updatedDownloadQueueService.remove 移除处于 WAITING 状态的任务时只发出 task_removed,不发出 task_updated。此时该闭包保持连接,后端线程必须等到 300 秒超时才继续。建议同时监听 task_removed

🛠️ 建议修改
         def _on_task_updated(updated: ResourceDownloadTask) -> None:
             if updated.task_key != task.task_key or updated.state not in terminal_states:
                 return
+            _disconnect()
+            self._respond_ocr_request(
+                request,
+                updated.state == ResourceDownloadTaskState.SUCCEEDED,
+                remember,
+            )
+
+        def _on_task_removed(removed_key: str) -> None:
+            if removed_key != task.task_key:
+                return
+            _disconnect()
+            self._respond_ocr_request(request, False, remember)
+
+        def _disconnect() -> None:
             with contextlib.suppress(RuntimeError):
                 self.queue.task_updated.disconnect(_on_task_updated)
-            self._respond_ocr_request(
-                request,
-                updated.state == ResourceDownloadTaskState.SUCCEEDED,
-                remember,
-            )
+            with contextlib.suppress(RuntimeError):
+                self.queue.task_removed.disconnect(_on_task_removed)
 
         self.queue.task_updated.connect(_on_task_updated)
+        self.queue.task_removed.connect(_on_task_removed)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _on_task_updated(updated: ResourceDownloadTask) -> None:
if updated.task_key != task.task_key or updated.state not in terminal_states:
return
with contextlib.suppress(RuntimeError):
self.queue.task_updated.disconnect(_on_task_updated)
self._respond_ocr_request(
request,
updated.state == ResourceDownloadTaskState.SUCCEEDED,
remember,
)
self.queue.task_updated.connect(_on_task_updated)
def _on_task_updated(updated: ResourceDownloadTask) -> None:
if updated.task_key != task.task_key or updated.state not in terminal_states:
return
_disconnect()
self._respond_ocr_request(
request,
updated.state == ResourceDownloadTaskState.SUCCEEDED,
remember,
)
def _on_task_removed(removed_key: str) -> None:
if removed_key != task.task_key:
return
_disconnect()
self._respond_ocr_request(request, False, remember)
def _disconnect() -> None:
with contextlib.suppress(RuntimeError):
self.queue.task_updated.disconnect(_on_task_updated)
with contextlib.suppress(RuntimeError):
self.queue.task_removed.disconnect(_on_task_removed)
self.queue.task_updated.connect(_on_task_updated)
self.queue.task_removed.connect(_on_task_removed)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/services/resource_update_coordinator.py` around lines 325 -
336, Update the OCR request flow around `_on_task_updated` to also listen for
`queue.task_removed`, so removal of the matching task completes the request
immediately instead of waiting for the timeout. Reuse the existing task-key
matching, callback disconnection, and `_respond_ocr_request` logic, returning a
failure response for removed tasks while preserving terminal-state success
handling.

Comment thread src/one_dragon_qt/view/setting/setting_env_interface.py Outdated
Comment thread src/one_dragon_qt/view/setting/setting_instance_interface.py Outdated
Comment thread src/one_dragon_qt/widgets/download_card/launcher_download_card.py
Comment thread src/one_dragon/base/operation/one_dragon_context.py
Comment thread src/one_dragon/base/operation/one_dragon_context.py
Comment on lines +176 to +185
try:
self.prepare_launcher_update(launcher_type)
staged_exe.replace(work_dir / staged_exe.name)
if launcher_type == 'runtime':
staged_runtime.rename(work_dir / RUNTIME_DIR)
self.finish_launcher_update(launcher_type, True)
shutil.rmtree(staging_dir, ignore_errors=True)
except Exception:
self.finish_launcher_update(launcher_type, False)
raise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

备份失败时仍会继续替换启动器。

_swap_launcher_backup 在第 227-228 行和第 244-245 行捕获异常后只写日志,不向上抛出。因此 prepare_launcher_update 在备份 exe 或 .runtime 目录失败时也会正常返回。随后 apply_staged_launcher_update 继续执行替换。如果后续步骤失败,finish_launcher_update(launcher_type, False) 找不到可用备份,用户会同时失去旧版本和新版本。

建议让备份阶段的失败可被感知:_swap_launcher_backupbackup=True 时抛出异常,回滚路径继续保持只记录日志。

🛠️ 参考修改方向
     def _swap_launcher_backup(self, launcher_type: LauncherType, backup: bool) -> None:
-        """备份或回滚启动器文件。"""
+        """备份或回滚启动器文件。备份失败时抛出异常,回滚失败时仅记录日志。"""
         work_dir = Path(os_utils.get_work_dir())
         action = '备份' if backup else '回滚'
@@
             except Exception as e:
                 log.error(f'{action}文件失败 {source_path.name}: {e}')
+                if backup:
+                    raise
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon/envs/update_service.py` around lines 176 - 185, Update
_swap_launcher_backup so exceptions are re-raised when backup=True, while
retaining log-only handling for rollback operations; ensure
prepare_launcher_update propagates backup failures and
apply_staged_launcher_update does not replace the launcher unless backups
complete successfully.

Comment on lines +143 to +144
# 读超时保持较短,避免网络停滞时取消请求长时间无响应。
with opener.open(request, timeout=5) as response:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

请确认 5 秒读超时不会让慢速网络用户无法完成下载。

opener.open(request, timeout=5) 同时作为连接超时和 socket 读超时。设计文档 docs/develop/one_dragon/modules/resource_download.md 第 67 行说明“最后一个候选源不限速,避免慢网络完全无法下载”。但 5 秒读超时不受 min_bytes_per_second 控制,对最后一个候选源同样生效。若某次 response.read() 在 5 秒内没有返回任何字节,下载会直接失败,即使整体速率尚可接受。

请确认该取值在弱网环境下经过验证,或考虑把读超时放宽到 15–30 秒,仅依赖 min_bytes_per_second 做低速淘汰。

🧰 Tools
🪛 Ruff (0.16.0)

[warning] 143-143: Comment contains ambiguous (FULLWIDTH COMMA). Did you mean , (COMMA)?

(RUF003)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon/utils/http_utils.py` around lines 143 - 144, 调整 http 下载流程中
opener.open 的 timeout 配置,避免 5 秒读超时使弱网或最后候选源在短暂无数据时失败;将读超时放宽至 15–30 秒,并继续由
min_bytes_per_second 负责低速下载淘汰,同时保持连接超时控制。

Comment thread src/zzz_od/gui/app.py
@ShadowLemoon
ShadowLemoon force-pushed the refactor/resource-download branch from 028807c to 44ccef6 Compare August 1, 2026 17:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (4)
src/one_dragon/base/operation/one_dragon_context.py (2)

518-525: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

OcrMatcher.init_model 基类签名缺少两个参数。

此处传入 on_source_failurefallback_on_slow。基类 src/one_dragon/base/matcher/ocr/ocr_matcher.pyinit_model 只声明到 on_source_success。其他实现类会抛出 TypeError。请把这两个参数补进基类签名。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon/base/operation/one_dragon_context.py` around lines 518 - 525,
Update the base OcrMatcher.init_model signature to accept on_source_failure and
fallback_on_slow, matching the arguments passed by OneDragonContext and existing
implementations; preserve the current behavior and parameter ordering for the
other arguments.

508-512: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

取消下载后 OCR 识别入口缺少懒加载保护。

init_ocr() 在用户取消时直接返回,不调用 init_model()OnnxOcrMatcher.ocr() 直接访问 self._model,此时 self._modelNone,识别会抛出 AttributeError。请在 OnnxOcrMatcher.ocr() 中按需调用 init_model()

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon/base/operation/one_dragon_context.py` around lines 508 - 512,
Update OnnxOcrMatcher.ocr() to lazily call init_model() before accessing
self._model, so OCR remains safe when init_ocr() returns after a cancelled
download. Preserve the existing recognition flow once the model is initialized.
src/one_dragon_qt/services/resource_update_coordinator.py (1)

325-336: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

任务被移出队列时 OCR 请求得不到应答。

_on_task_updated 只监听 task_updatedDownloadQueueService.remove 移除 WAITING 状态任务时只发出 task_removed。此时闭包保持连接,后端线程必须等到 300 秒超时才继续。请同时监听 task_removed

🛠️ 建议修改
         def _on_task_updated(updated: ResourceDownloadTask) -> None:
             if updated.task_key != task.task_key or updated.state not in terminal_states:
                 return
-            with contextlib.suppress(RuntimeError):
-                self.queue.task_updated.disconnect(_on_task_updated)
+            _disconnect()
             self._respond_ocr_request(
                 request,
                 updated.state == ResourceDownloadTaskState.SUCCEEDED,
                 remember,
             )
 
+        def _on_task_removed(removed_key: str) -> None:
+            if removed_key != task.task_key:
+                return
+            _disconnect()
+            self._respond_ocr_request(request, False, remember)
+
+        def _disconnect() -> None:
+            with contextlib.suppress(RuntimeError):
+                self.queue.task_updated.disconnect(_on_task_updated)
+            with contextlib.suppress(RuntimeError):
+                self.queue.task_removed.disconnect(_on_task_removed)
+
         self.queue.task_updated.connect(_on_task_updated)
+        self.queue.task_removed.connect(_on_task_removed)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/services/resource_update_coordinator.py` around lines 325 -
336, Update the OCR request callback around _on_task_updated to also handle the
queue’s task_removed signal, so removal of the matching task completes the
request instead of waiting for timeout. Reuse the existing task-key matching,
disconnection, and _respond_ocr_request flow, and connect the removal signal
alongside task_updated while preserving terminal-state handling for updates.
src/one_dragon_qt/services/download_queue_service.py (1)

324-345: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

进度回调需要绑定发出进度的任务。

_on_progress_changed 读取 self._current_task,不使用信号来源任务。_on_worker_finished 在第 384 行立即调用 _start_next() 并切换 _current_task。旧 worker 尚未派发的 progress_changed 事件随后会写入新任务的 progress,并把新任务状态改回 DOWNLOADING。请在连接时绑定任务对象。

🛠️ 建议修改
         worker = DownloadTaskWorker(self.ctx, task)
-        worker.progress_changed.connect(self._on_progress_changed)
+        worker.progress_changed.connect(
+            lambda progress, bound_task=task: self._on_progress_changed(bound_task, progress)
+        )
-    def _on_progress_changed(self, progress: ResourceDownloadProgress) -> None:
-        """保存当前任务进度。"""
-        task = self._current_task
-        if task is None:
+    def _on_progress_changed(
+        self,
+        task: ResourceDownloadTask,
+        progress: ResourceDownloadProgress,
+    ) -> None:
+        """保存指定任务的进度。"""
+        if task is not self._current_task:
             return
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/services/download_queue_service.py` around lines 324 - 345,
Update the progress signal connection in the worker-start flow to bind the
worker’s task object when connecting to _on_progress_changed, and change
_on_progress_changed to accept and update that bound task instead of reading
self._current_task. Preserve the existing phase and cancellation state
transitions while ensuring late progress events from a finished worker cannot
modify the next task.
🧹 Nitpick comments (7)
src/one_dragon_qt/services/download_queue_service.py (1)

55-69: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

建议给任务数据类关闭 __eq__ 生成。

ResourceDownloadTask 是 dataclass,默认生成按字段值比较的 __eq__self._current_task in self.tasksself.tasks.index(self._current_task) 因此按值匹配,而不是按身份匹配。当前 queued_at 时间戳使不同实例几乎总是不相等,所以尚未出现故障。加上 eq=False 可以让这些查找严格按身份进行。

♻️ 建议修改
-@dataclass(slots=True)
+@dataclass(slots=True, eq=False)
 class ResourceDownloadTask:
     """下载队列中的任务及运行状态。"""

Also applies to: 174-176, 239-243

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/services/download_queue_service.py` around lines 55 - 69,
Update the ResourceDownloadTask dataclass declaration with eq=False so it uses
identity-based equality. Preserve the existing task_key and field definitions,
ensuring self._current_task membership and index lookups in the task queue match
only the same task instance.
src/one_dragon/base/operation/context_download_request_event.py (1)

21-23: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

建议把内部状态字段标记为 init=False

_response_event_confirmed_remember 目前进入 __init__ 参数列表,调用方可以直接传入内部状态。这三个字段只由 respond() 维护。加上 init=False 可以收窄构造接口。

♻️ 建议修改
-    _response_event: threading.Event = field(default_factory=threading.Event)
-    _confirmed: bool = False
-    _remember: bool = False
+    _response_event: threading.Event = field(default_factory=threading.Event, init=False)
+    _confirmed: bool = field(default=False, init=False)
+    _remember: bool = field(default=False, init=False)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon/base/operation/context_download_request_event.py` around lines
21 - 23, 将 ContextDownloadRequestEvent 中的内部状态字段 _response_event、_confirmed 和
_remember 的 dataclass field 配置为 init=False,使它们不再出现在构造函数参数中;保留现有默认值,并继续由
respond() 负责维护这些状态。
src/one_dragon_qt/windows/main_app_window_base.py (1)

170-180: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

建议改用公共钩子,替代反射调用私有方法。

这里用 getattr 查找子界面的 _update_developer_visibility。跨类访问私有名会在界面重命名该方法时静默失效,没有任何报错。建议在 BaseInterface 上定义一个空实现的公共方法(例如 update_developer_visibility()),各界面按需覆盖,这里直接调用。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/windows/main_app_window_base.py` around lines 170 - 180, 在
BaseInterface 中新增空实现的公共钩子 update_developer_visibility(),并让需要更新开发者可见性的界面覆盖它;同时修改
_apply_developer_mode_visibility,移除 getattr 和 callable 反射逻辑,直接对每个 stackedWidget
子界面调用该公共方法。
src/one_dragon_qt/services/resource_update_coordinator.py (1)

297-299: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

建议按 resource_type 判断 OCR 项,而不是按 key 前缀。

key.startswith('ocr:') 依赖 task_key 的字符串格式。若某个模型的 config_key 命名改动,这个判断容易失效。这里已经可以直接遍历 specs 判断 resource_type

♻️ 建议修改
-            if not any(key.startswith('ocr:') for key in selected_keys):
+            if not any(spec.resource_type == 'ocr' for spec in specs):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/services/resource_update_coordinator.py` around lines 297 -
299, 将 OCR 请求完成判断从 selected_keys 中检查 “ocr:” 前缀改为遍历已有的 specs,并依据对应项的
resource_type 判断是否包含 OCR 资源;仅当没有任何 OCR 类型资源时调用 _respond_ocr_request(ocr_request,
False, False),避免依赖 task_key/config_key 的命名格式。
src/one_dragon_qt/view/resource_management_interface.py (1)

446-446: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

避免跨类访问 _launcher_type 私有属性。

第 446 行读取 LauncherDownloadCard._launcher_type。该名称以下划线开头,属于内部状态。若卡片内部重命名,此处会静默失效。

建议在 LauncherDownloadCard 上暴露公开只读属性(例如 launcher_type),并在此处使用它。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/view/resource_management_interface.py` at line 446, Update
LauncherDownloadCard to expose a public read-only launcher_type property backed
by its internal _launcher_type value, then change the caller at the
launcher_type assignment to use self.launcher_opt.launcher_type instead of
accessing the private attribute directly.
src/one_dragon/envs/update_service.py (1)

153-160: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

建议对 target_version 做更严格的路径净化。

第 159 行只替换了 /\。如果标签包含 ..safe_version 仍可以让目录跳出 launcher_updatetarget_version 来自远程仓库标签,属于外部输入。

建议追加过滤,只保留安全字符。

🛡️ 建议修改
-        safe_version = target_version.replace('/', '_').replace('\\', '_')
+        safe_version = re.sub(r'[^A-Za-z0-9._-]', '_', target_version).strip('.') or 'latest'

需要在文件顶部导入 re

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon/envs/update_service.py` around lines 153 - 160, Update
get_launcher_staging_dir to sanitize the externally supplied target_version by
retaining only an explicitly safe character set, preventing values such as “..”
from escaping the launcher_update directory; add the required re import and use
the sanitized version when constructing the Path.
src/one_dragon_qt/widgets/setting_card/common_download_card.py (1)

91-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

修正 extra_btn_list 的隐式 Optional 注解。 两处构造函数都写成 list[QAbstractButton] = None。PEP 484 不允许隐式 Optional,Ruff 也报 RUF013。本次改动已经把其他参数改为现代联合类型,这两处应保持一致。

  • src/one_dragon_qt/widgets/setting_card/common_download_card.py#L91-L91:把 CommonDownloaderSettingCard.__init__extra_btn_list: list[QAbstractButton] = None 改为 extra_btn_list: list[QAbstractButton] | None = None
  • src/one_dragon_qt/widgets/setting_card/common_download_card.py#L391-L391:把 ZipDownloaderSettingCard.__init__ 的同名参数做相同修改。
♻️ 建议修改
-            extra_btn_list: list[QAbstractButton] = None,
+            extra_btn_list: list[QAbstractButton] | None = None,

As per coding guidelines:“所有函数签名和类成员变量都必须有类型注解,并优先使用 list[str]X | Y 等现代语法。”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/widgets/setting_card/common_download_card.py` at line 91,
更新 src/one_dragon_qt/widgets/setting_card/common_download_card.py 第91行的
CommonDownloaderSettingCard.__init__ 以及第391行的
ZipDownloaderSettingCard.__init__,将 extra_btn_list 的类型注解改为显式可空的
list[QAbstractButton] | None,并保留默认值 None。

Sources: Coding guidelines, Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/one_dragon_qt/view/resource_management_interface.py`:
- Around line 498-513: Update _on_ocr_gpu_changed so ctx.init_ocr() runs
asynchronously in a background worker instead of blocking the GUI thread, while
preserving the immediate model_config.ocr_use_gpu update. Keep any
configuration-control state changes and user notifications on the main thread,
using the existing worker/signaling mechanism and ensuring completion or failure
restores the appropriate UI state.

In `@src/one_dragon_qt/widgets/download_card/launcher_download_card.py`:
- Around line 77-92: Update _on_type_changed so replacing the version checker
does not release the previous checker while its QThread is still running. Prefer
reusing the existing version_checker and updating its launcher type when the
thread is not running; otherwise retain the old checker until its worker thread
finishes before allowing it to be destroyed, while preserving signal handling
and triggering the refreshed version check.

In `@src/one_dragon_qt/widgets/setting_card/common_download_card.py`:
- Around line 330-333: Update the download action around download_queue.enqueue
so completed tasks, including SUCCEEDED tasks, are removed from the queue before
being re-enqueued; retain the existing retry behavior for CANCELLED tasks.
Ensure the subsequent enqueue creates a fresh task that can download again
rather than silently reusing the completed task.

In `@src/one_dragon_qt/windows/main_app_window_base.py`:
- Around line 228-243: Update closeEvent to distinguish the user’s response:
when No is selected, ignore the event and return; when Yes is selected, record a
pending-close intent, call download_queue.cancel_all(), and wait for
queue_updated to report no active tasks before closing. Initialize the intent in
__init__ and connect the existing queue_updated signal to the handler that
completes the deferred close.

In `@src/one_dragon/base/web/common_downloader.py`:
- Around line 116-117: 在处理 GITHUB_SOURCE_ID 的 ghproxy_url 拼接逻辑中,先移除 ghproxy_url
末尾的斜杠,再使用现有的单斜杠拼接 download_url,确保用户输入带或不带尾部斜杠时都不会生成双斜杠地址。

---

Duplicate comments:
In `@src/one_dragon_qt/services/download_queue_service.py`:
- Around line 324-345: Update the progress signal connection in the worker-start
flow to bind the worker’s task object when connecting to _on_progress_changed,
and change _on_progress_changed to accept and update that bound task instead of
reading self._current_task. Preserve the existing phase and cancellation state
transitions while ensuring late progress events from a finished worker cannot
modify the next task.

In `@src/one_dragon_qt/services/resource_update_coordinator.py`:
- Around line 325-336: Update the OCR request callback around _on_task_updated
to also handle the queue’s task_removed signal, so removal of the matching task
completes the request instead of waiting for timeout. Reuse the existing
task-key matching, disconnection, and _respond_ocr_request flow, and connect the
removal signal alongside task_updated while preserving terminal-state handling
for updates.

In `@src/one_dragon/base/operation/one_dragon_context.py`:
- Around line 518-525: Update the base OcrMatcher.init_model signature to accept
on_source_failure and fallback_on_slow, matching the arguments passed by
OneDragonContext and existing implementations; preserve the current behavior and
parameter ordering for the other arguments.
- Around line 508-512: Update OnnxOcrMatcher.ocr() to lazily call init_model()
before accessing self._model, so OCR remains safe when init_ocr() returns after
a cancelled download. Preserve the existing recognition flow once the model is
initialized.

---

Nitpick comments:
In `@src/one_dragon_qt/services/download_queue_service.py`:
- Around line 55-69: Update the ResourceDownloadTask dataclass declaration with
eq=False so it uses identity-based equality. Preserve the existing task_key and
field definitions, ensuring self._current_task membership and index lookups in
the task queue match only the same task instance.

In `@src/one_dragon_qt/services/resource_update_coordinator.py`:
- Around line 297-299: 将 OCR 请求完成判断从 selected_keys 中检查 “ocr:” 前缀改为遍历已有的
specs,并依据对应项的 resource_type 判断是否包含 OCR 资源;仅当没有任何 OCR 类型资源时调用
_respond_ocr_request(ocr_request, False, False),避免依赖 task_key/config_key 的命名格式。

In `@src/one_dragon_qt/view/resource_management_interface.py`:
- Line 446: Update LauncherDownloadCard to expose a public read-only
launcher_type property backed by its internal _launcher_type value, then change
the caller at the launcher_type assignment to use
self.launcher_opt.launcher_type instead of accessing the private attribute
directly.

In `@src/one_dragon_qt/widgets/setting_card/common_download_card.py`:
- Line 91: 更新 src/one_dragon_qt/widgets/setting_card/common_download_card.py
第91行的 CommonDownloaderSettingCard.__init__ 以及第391行的
ZipDownloaderSettingCard.__init__,将 extra_btn_list 的类型注解改为显式可空的
list[QAbstractButton] | None,并保留默认值 None。

In `@src/one_dragon_qt/windows/main_app_window_base.py`:
- Around line 170-180: 在 BaseInterface 中新增空实现的公共钩子
update_developer_visibility(),并让需要更新开发者可见性的界面覆盖它;同时修改
_apply_developer_mode_visibility,移除 getattr 和 callable 反射逻辑,直接对每个 stackedWidget
子界面调用该公共方法。

In `@src/one_dragon/base/operation/context_download_request_event.py`:
- Around line 21-23: 将 ContextDownloadRequestEvent 中的内部状态字段
_response_event、_confirmed 和 _remember 的 dataclass field 配置为
init=False,使它们不再出现在构造函数参数中;保留现有默认值,并继续由 respond() 负责维护这些状态。

In `@src/one_dragon/envs/update_service.py`:
- Around line 153-160: Update get_launcher_staging_dir to sanitize the
externally supplied target_version by retaining only an explicitly safe
character set, preventing values such as “..” from escaping the launcher_update
directory; add the required re import and use the sanitized version when
constructing the Path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cc7c18e7-f6e4-490c-a63d-f15d3947548c

📥 Commits

Reviewing files that changed from the base of the PR and between 028807c and 44ccef6.

📒 Files selected for processing (49)
  • config/repository.yml
  • deploy/module_manifest.py
  • docs/develop/README.md
  • docs/develop/one_dragon/modules/resource_download.md
  • src/one_dragon/base/config/basic_model_config.py
  • src/one_dragon/base/matcher/ocr/ocr_matcher.py
  • src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
  • src/one_dragon/base/operation/context_download_request_event.py
  • src/one_dragon/base/operation/context_event_bus.py
  • src/one_dragon/base/operation/one_dragon_context.py
  • src/one_dragon/base/operation/one_dragon_env_context.py
  • src/one_dragon/base/web/common_downloader.py
  • src/one_dragon/base/web/zip_downloader.py
  • src/one_dragon/envs/env_config.py
  • src/one_dragon/envs/repo_config.py
  • src/one_dragon/envs/update_service.py
  • src/one_dragon/utils/http_utils.py
  • src/one_dragon/yolo/yolo_utils.py
  • src/one_dragon_qt/_rc/qss/dark/title_bar.qss
  • src/one_dragon_qt/_rc/qss/light/title_bar.qss
  • src/one_dragon_qt/_rc/resource.py
  • src/one_dragon_qt/services/download_queue_service.py
  • src/one_dragon_qt/services/resource_update_coordinator.py
  • src/one_dragon_qt/view/code_interface.py
  • src/one_dragon_qt/view/resource_management_interface.py
  • src/one_dragon_qt/view/setting/resource_download_interface.py
  • src/one_dragon_qt/view/setting/setting_env_interface.py
  • src/one_dragon_qt/view/setting/setting_instance_interface.py
  • src/one_dragon_qt/view/source_config_interface.py
  • src/one_dragon_qt/widgets/download_card/launcher_download_card.py
  • src/one_dragon_qt/widgets/download_queue_dialog.py
  • src/one_dragon_qt/widgets/install_card/launcher_install_card.py
  • src/one_dragon_qt/widgets/resource_download_dialog.py
  • src/one_dragon_qt/widgets/setting_card/common_download_card.py
  • src/one_dragon_qt/widgets/teaching_tip.py
  • src/one_dragon_qt/windows/main_app_window_base.py
  • src/one_dragon_qt/windows/window.py
  • src/zzz_od/application/hollow_zero/lost_void/context/lost_void_context.py
  • src/zzz_od/application/hollow_zero/lost_void/context/lost_void_detector.py
  • src/zzz_od/auto_battle/auto_battle_dodge_context.py
  • src/zzz_od/config/model_config.py
  • src/zzz_od/context/zzz_context.py
  • src/zzz_od/gui/app.py
  • src/zzz_od/gui/view/home/home_interface.py
  • src/zzz_od/gui/view/setting/app_setting_interface.py
  • src/zzz_od/gui/view/setting/zzz_resource_download_interface.py
  • src/zzz_od/hollow_zero/hollow_map/hollow_zero_map_service.py
  • src/zzz_od/yolo/flash_classifier.py
  • src/zzz_od/yolo/hollow_event_detector.py
💤 Files with no reviewable changes (6)
  • src/one_dragon/yolo/yolo_utils.py
  • src/one_dragon_qt/view/code_interface.py
  • src/one_dragon_qt/view/setting/resource_download_interface.py
  • src/zzz_od/gui/view/setting/zzz_resource_download_interface.py
  • src/zzz_od/gui/view/setting/app_setting_interface.py
  • src/zzz_od/config/model_config.py
🚧 Files skipped from review as they are similar to previous changes (27)
  • src/one_dragon/base/operation/context_event_bus.py
  • deploy/module_manifest.py
  • src/zzz_od/auto_battle/auto_battle_dodge_context.py
  • src/one_dragon_qt/widgets/teaching_tip.py
  • src/one_dragon_qt/_rc/qss/light/title_bar.qss
  • src/one_dragon_qt/view/source_config_interface.py
  • src/one_dragon_qt/_rc/resource.py
  • src/one_dragon_qt/view/setting/setting_instance_interface.py
  • src/one_dragon_qt/_rc/qss/dark/title_bar.qss
  • src/zzz_od/context/zzz_context.py
  • src/zzz_od/application/hollow_zero/lost_void/context/lost_void_context.py
  • src/zzz_od/yolo/hollow_event_detector.py
  • src/one_dragon/base/matcher/ocr/ocr_matcher.py
  • src/one_dragon/envs/env_config.py
  • src/zzz_od/yolo/flash_classifier.py
  • src/one_dragon/base/operation/one_dragon_env_context.py
  • src/zzz_od/hollow_zero/hollow_map/hollow_zero_map_service.py
  • src/zzz_od/gui/view/home/home_interface.py
  • src/zzz_od/application/hollow_zero/lost_void/context/lost_void_detector.py
  • src/one_dragon_qt/widgets/install_card/launcher_install_card.py
  • src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
  • src/one_dragon_qt/widgets/download_queue_dialog.py
  • config/repository.yml
  • src/zzz_od/gui/app.py
  • src/one_dragon_qt/windows/window.py
  • src/one_dragon/base/web/zip_downloader.py
  • src/one_dragon/base/config/basic_model_config.py

Comment on lines +498 to +513
def _after_model_download(
self,
success: bool,
config_key: str,
target: str,
) -> None:
"""模型下载成功后切换配置。"""
if success:
self.ctx.model_config.update(config_key, target)
if config_key == 'ocr':
self.ctx.init_ocr()

def _on_ocr_gpu_changed(self, value: bool) -> None:
"""更新 OCR GPU 配置。"""
self.ctx.model_config.ocr_use_gpu = value
self.ctx.init_ocr()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 确认 init_ocr 的实现与线程假设
ast-grep run --pattern $'def init_ocr(self) -> None {
  $$$
}' --lang python src/one_dragon/base/operation/one_dragon_context.py
rg -n -C 5 'def init_ocr|def _confirm_resource_download|def dispatch_event' src/one_dragon/base/operation/

Repository: OneDragon-Anything/ZenlessZoneZero-OneDragon

Length of output: 3160


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate relevant files =="
fd -a 'one_dragon_context.py|context_event_bus.py|resource_management_interface.py|worker.*|download.*' . | sed 's#^\./##' | head -100

echo
echo "== init_ocr outline and implementation =="
wc -l src/one_dragon/base/operation/one_dragon_context.py
sed -n '470,540p' src/one_dragon/base/operation/one_dragon_context.py

echo
echo "== context_event_bus implementation =="
wc -l src/one_dragon/base/operation/context_event_bus.py
cat -n src/one_dragon/base/operation/context_event_bus.py

echo
echo "== resource_management relevant sections =="
sed -n '450,525p' src/one_dragon_qt/view/resource_management_interface.py

echo
echo "== search init_ocr call sites =="
rg -n -C 4 'init_ocr\(' src/one_dragon src/one_dragon_qt

Repository: OneDragon-Anything/ZenlessZoneZero-OneDragon

Length of output: 13050


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== DownloadWorker implementation and after_download callbacks =="
rg -n -C 8 'class DownloadWorker|after_download|after_download=' src/one_dragon_qt services/view -g '*.py' 2>/dev/null || rg -n -C 8 'class DownloadWorker|after_download|after_download=' . -g '*.py' | head -200

echo
echo "== _on_ocr_gpu_changed call sites / GUI wiring =="
rg -n -C 6 'ocr_use_gpu|is_gpu|GPU|ocr' src/one_dragon_qt/view/resource_management_interface.py src/one_dragon_qt -g '*.py' | head -240

echo
echo "== OnnxOcrMatcher implementation =="
rg -n -C 8 'class OnnxOcrMatcher|class OnnxOcrParam|def init_model|def is_file_existed|cleanup' src -g '*.py' | head -320

echo
echo "== event dispatch callback implementations for ContextNotifyEvent =="
rg -n -C 5 'ContextNotifyEvent\.EVENT_ID|def .*notify|notify_config|ContextNotifyEvent' src/one_dragon src/one_dragon_qt -g '*.py' | head -320

Repository: OneDragon-Anything/ZenlessZoneZero-OneDragon

Length of output: 50400


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== exact OnnxOcrMatcher declaration and methods =="
rg -n 'class OnnxOcrMatcher|class OnnxOcrParam|def init_model|def is_file_existed|def cleanup|__init__' src -g '*.py' | sed -n '1,220p'

echo
echo "== init_ocr source with line numbers =="
sed -n '486,538p' src/one_dragon/base/operation/one_dragon_context.py

echo
echo "== context notify event ID and listeners =="
rg -n 'ContextNotifyEvent\.EVENT_ID|listen_event\(".*ContextNotifyEvent|listen_event\(.*ContextNotifyEvent|def .*context.*notify|_notify' src/one_dragon src/one_dragon_qt -g '*.py' | sed -n '1,220p'

echo
echo "== thread safety indicators in OCR model code =="
sed -n '1,220p' src/one_dragon_qt/widgets/onnx_ocr_matcher.py

Repository: OneDragon-Anything/ZenlessZoneZero-OneDragon

Length of output: 37365


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== exact OnnxOcrMatcher file =="
fd -a 'onnx.*ocr.*matcher|ocr.*matcher|matcher|paddleocr|onnx_paddleocr' src onnxocr 2>/dev/null | sed 's#^\./##' | head -120

echo
echo "== OnnxOcrMatcher declarations =="
rg -n --glob '*.py' 'class OnnxOcrMatcher|class OnnxOcrParam|def init_model\b|def is_file_existed\b|def cleanup\b|__init__\(' src onnxocr | sed -n '1,180p'

echo
echo "== init_model implementations (focused) =="
rg -n --glob '*.py' -C 3 'def init_model\b|OnnxOcrMatcher\b|class OnnxOcrMatcher' src onnxocr | sed -n '1,320p'

Repository: OneDragon-Anything/ZenlessZoneZero-OneDragon

Length of output: 18668


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== OCR matcher implementation =="
wc -l src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
sed -n '1,240p' src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py

echo
echo "== ModelResourceDefinition GPU config reference =="
rg -n -C 8 'class ModelResourceDefinition|gpu_config_key|onnxocr|OnnxOcrParam|OnnxOcrMatcher' src/one_dragon_qt src/one_dragon -g '*.py' | sed -n '1,320p'

echo
echo "== main_app_window context notify queue implementation =="
sed -n '1,170p' src/one_dragon_qt/windows/main_app_window_base.py

Repository: OneDragon-Anything/ZenlessZoneZero-OneDragon

Length of output: 41503


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== full OnnxOcrMatcher cleanup and relevant methods =="
sed -n '208,380p' src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
sed -n '520,580p' src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py

echo
echo "== main app signal definitions =="
rg -n -C 4 'context_notify_signal\s*=|context_download_request_signal\s*=|init_ocr\(' src/one_dragon_qt/windows src -g '*.py' | sed -n '1,220p'

Repository: OneDragon-Anything/ZenlessZoneZero-OneDragon

Length of output: 25008


init_ocr() 后台执行,避免 GUI 线程被 OCR 模型加载阻塞。

_on_ocr_gpu_changed 在主线程同步切换到 ctx.init_ocr(),而该方法会执行 OCR 下载和 ONNXPaddleOcr 加载;init_model() 内部有 _init_lock,但当前调用仍在 GUI 线程等待完成,可能导致界面卡住。建议将 OCR 模型重载放入后台线程,只在主线程更新配置按钮状态和显示通知。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/view/resource_management_interface.py` around lines 498 -
513, Update _on_ocr_gpu_changed so ctx.init_ocr() runs asynchronously in a
background worker instead of blocking the GUI thread, while preserving the
immediate model_config.ocr_use_gpu update. Keep any configuration-control state
changes and user notifications on the main thread, using the existing
worker/signaling mechanism and ensuring completion or failure restores the
appropriate UI state.

Comment thread src/one_dragon_qt/widgets/download_card/launcher_download_card.py
Comment on lines +330 to +333
if task is not None and task.state == ResourceDownloadTaskState.CANCELLED:
self.download_queue.retry(task.task_key)
else:
self.download_queue.enqueue(spec)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

已成功的同版本任务重新入队后不会重新下载。

DownloadQueueService.enqueue 先按 task_key 查找已有任务,命中则直接返回,不重置状态。第 333 行对 SUCCEEDED 状态的任务调用 enqueue,因此不会触发新的下载。之后第 336 行仍显示“已加入队列”提示,用户会误判任务已开始。

正常情况下 is_downloaded 为 True 会禁用按钮,掩盖该路径。但如果资源文件被手动删除,按钮重新可用,点击后就会静默无动作。

建议对已结束的任务先移除再入队。

🐛 建议修复
             if task is not None and task.state == ResourceDownloadTaskState.CANCELLED:
                 self.download_queue.retry(task.task_key)
             else:
+                if task is not None:
+                    self.download_queue.remove(task.task_key)
                 self.download_queue.enqueue(spec)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/widgets/setting_card/common_download_card.py` around lines
330 - 333, Update the download action around download_queue.enqueue so completed
tasks, including SUCCEEDED tasks, are removed from the queue before being
re-enqueued; retain the existing retry behavior for CANCELLED tasks. Ensure the
subsequent enqueue creates a fresh task that can download again rather than
silently reusing the completed task.

Comment thread src/one_dragon_qt/windows/main_app_window_base.py
Comment on lines +116 to +117
if source_id == GITHUB_SOURCE_ID and ghproxy_url is not None:
download_url = f'{ghproxy_url}/{download_url}'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

请确认 ghproxy_url 尾部斜杠不会产生双斜杠地址。

第 117 行固定拼接 /。界面对免费代理地址的占位符是 https://ghproxy.link/,用户很可能输入带尾部斜杠的地址。此时结果为 https://ghproxy.link//https://github.com/...。部分代理会拒绝该形式。

建议先去掉尾部斜杠再拼接。

🛠️ 建议修改
-            if source_id == GITHUB_SOURCE_ID and ghproxy_url is not None:
-                download_url = f'{ghproxy_url}/{download_url}'
+            if source_id == GITHUB_SOURCE_ID and ghproxy_url is not None:
+                download_url = f'{ghproxy_url.rstrip("/")}/{download_url}'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if source_id == GITHUB_SOURCE_ID and ghproxy_url is not None:
download_url = f'{ghproxy_url}/{download_url}'
if source_id == GITHUB_SOURCE_ID and ghproxy_url is not None:
download_url = f'{ghproxy_url.rstrip("/")}/{download_url}'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon/base/web/common_downloader.py` around lines 116 - 117, 在处理
GITHUB_SOURCE_ID 的 ghproxy_url 拼接逻辑中,先移除 ghproxy_url 末尾的斜杠,再使用现有的单斜杠拼接
download_url,确保用户输入带或不带尾部斜杠时都不会生成双斜杠地址。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/one_dragon_qt/widgets/download_card/launcher_download_card.py (1)

186-190: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

将启动器类型和更新事务状态绑定到队列任务。

队列下单时 _on_download_click 直接调用基类回调并跳过 prepare_launcher_update(a)。如果队列支持多并发任务,任务完成时会读取当前卡片的 _launcher_type 调用 finish_launcher_update,会把当前选择的启动器类型当作实际任务类型结算。把任务当时的下载配置/准备状态保存在该任务上下文中,不要用卡片状态覆盖。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/widgets/download_card/launcher_download_card.py` around
lines 186 - 190, 修复 _on_download_click 与队列任务完成回调之间的状态传递:下单时不要跳过
prepare_launcher_update(a),应将该任务实际的启动器类型及下载配置/准备状态保存到任务上下文;任务完成时使用上下文中的快照调用
finish_launcher_update,禁止读取当前卡片的 _launcher_type 覆盖并发任务的实际类型。
src/one_dragon_qt/windows/main_app_window_base.py (1)

233-253: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

cancel_all() 后重新检查队列状态。

用户确认时后台任务可能已完成。用户选择 Yes 后,cancel_all() 不会进入可取消状态并触发更新;_closing_after_cancel 会在后续无更新时无法触发自动关闭,导致需要再次手动关闭。

请在 cancel_all() 后立即调用 self.download_queue.has_active_tasks()。如果队列已清空,直接完成当前 QCloseEvent;只有仍有活动任务时,才设置 _closing_after_cancel 并忽略事件。补充覆盖该时序的回归测试。

建议修改
-        self._closing_after_cancel = True
         self.download_queue.cancel_all()
+        if not self.download_queue.has_active_tasks():
+            super().closeEvent(event)
+            return
+        self._closing_after_cancel = True
         self.show_download_queue()
         event.ignore()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/windows/main_app_window_base.py` around lines 233 - 253, 在
closeEvent 的用户确认分支中,先调用 download_queue.cancel_all() 后立即重新检查
has_active_tasks();若队列已无活动任务,直接完成当前关闭事件并返回,避免依赖后续更新。仅当仍有活动任务时设置
_closing_after_cancel、显示下载队列并忽略事件;同时补充覆盖该竞态时序的回归测试。

Source: Coding guidelines

🧹 Nitpick comments (1)
src/one_dragon_qt/windows/main_app_window_base.py (1)

63-64: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

为新增实例成员补充显式类型注解。

self.download_queueself.download_queue_dialog 当前没有类型注解。请分别声明为 DownloadQueueServiceDownloadQueueDialog

建议修改
-        self.download_queue = DownloadQueueService(ctx)
+        self.download_queue: DownloadQueueService = DownloadQueueService(ctx)

-        self.download_queue_dialog = DownloadQueueDialog(self.download_queue, self)
+        self.download_queue_dialog: DownloadQueueDialog = DownloadQueueDialog(
+            self.download_queue,
+            self,
+        )

As per coding guidelines,Python 源码中的类成员变量必须有类型注解。

Also applies to: 74-75

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/one_dragon_qt/windows/main_app_window_base.py` around lines 63 - 64,
为实例成员补充显式类型注解:在初始化 self.download_queue 的位置声明其类型为 DownloadQueueService,并在对应的
self.download_queue_dialog 初始化位置声明其类型为 DownloadQueueDialog;保持现有初始化逻辑不变。

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/one_dragon_qt/widgets/download_card/launcher_download_card.py`:
- Around line 186-190: 修复 _on_download_click 与队列任务完成回调之间的状态传递:下单时不要跳过
prepare_launcher_update(a),应将该任务实际的启动器类型及下载配置/准备状态保存到任务上下文;任务完成时使用上下文中的快照调用
finish_launcher_update,禁止读取当前卡片的 _launcher_type 覆盖并发任务的实际类型。

In `@src/one_dragon_qt/windows/main_app_window_base.py`:
- Around line 233-253: 在 closeEvent 的用户确认分支中,先调用 download_queue.cancel_all()
后立即重新检查 has_active_tasks();若队列已无活动任务,直接完成当前关闭事件并返回,避免依赖后续更新。仅当仍有活动任务时设置
_closing_after_cancel、显示下载队列并忽略事件;同时补充覆盖该竞态时序的回归测试。

---

Nitpick comments:
In `@src/one_dragon_qt/windows/main_app_window_base.py`:
- Around line 63-64: 为实例成员补充显式类型注解:在初始化 self.download_queue 的位置声明其类型为
DownloadQueueService,并在对应的 self.download_queue_dialog 初始化位置声明其类型为
DownloadQueueDialog;保持现有初始化逻辑不变。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 55db96d6-d30e-4dfe-8f8d-c1abdd283345

📥 Commits

Reviewing files that changed from the base of the PR and between 44ccef6 and 88c060c.

📒 Files selected for processing (6)
  • src/one_dragon/base/matcher/ocr/ocr_matcher.py
  • src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
  • src/one_dragon_qt/view/setting/setting_env_interface.py
  • src/one_dragon_qt/view/setting/setting_instance_interface.py
  • src/one_dragon_qt/widgets/download_card/launcher_download_card.py
  • src/one_dragon_qt/windows/main_app_window_base.py
💤 Files with no reviewable changes (1)
  • src/one_dragon_qt/view/setting/setting_env_interface.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/one_dragon_qt/view/setting/setting_instance_interface.py
  • src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant