Skip to content

feat(update): 优化软件的更新流程,并顺便修复了更新日志打开就在最底下的问题 - #544

Open
bilixuesheng wants to merge 67 commits into
XiaoYouChR:mainfrom
bilixuesheng:feat/update-download-install
Open

feat(update): 优化软件的更新流程,并顺便修复了更新日志打开就在最底下的问题#544
bilixuesheng wants to merge 67 commits into
XiaoYouChR:mainfrom
bilixuesheng:feat/update-download-install

Conversation

@bilixuesheng

Copy link
Copy Markdown
Contributor

PR 描述

将"检测到新版本 → 立即下载"的体验从"当作普通下载任务"升级为完整的更新流程。

判断规则(两个入口通用):Windows 且安装包为 .exe → 完整自动流程;其余(.msi/.zip/macOS/Linux)→ 兜底走普通任务下载。

完整流程(Windows + .exe):

  • 下载到数据目录 APP_DATA_DIR/update不出现在下载列表(复用满血 8 线程下载引擎,但从不调用 taskService.add,因此不持久化到 tasks.jsonl、不发 taskAdded
  • 线程数 / 重分配大小 / AI 自动提速跟随用户全局设置preBlockNum / maxReassignSize / autoSpeedUp
  • 下载过程用 StateToolTip 实时显示进度,位置与其他提示一致
  • 下载成功 → 弹常驻 InfoBar 询问是否安装 → 点「立即安装」用 os.startfile 启动安装程序并退出自身;启动失败则不退出并给出错误提示
  • 下载失败 → StateToolTip 显示失败态,不弹对话框
  • 同一时刻只允许一个更新下载(模块级单飞防护)
  • 点「立即下载」瞬间关闭"检测到新版本"提示

两个入口汇入同一套 startUpdateDownload 逻辑:

  • "检测到新版本"提示的「立即下载」(自动选最匹配当前系统的包)
  • 版本更新详情窗口中手动选择安装包并确认

兜底方案(非 exe):沿用现有普通任务下载,进下载列表、落默认下载目录,提示"下载完成后请手动安装"。

update 文件夹在软件启动时与退出时各清空一次(best-effort,忽略正在运行、被占用的安装程序)。

同时修复:版本更新详情窗口"更新日志"文本框默认滚动到底部的问题,改为默认停在顶部(根因修复在 MarkdownViewer.setMarkdown,全局生效)。

PR 类型

  • 功能
    • Bug 修复

PR 检查清单

  • 应用成功启动且测试无 Bug
  • 包含破坏式更新

未改动 Task 模型 / 序列化 / taskService,旧版本升级上来的 tasks.jsonl 存档读取零风险。

备注

  • 已验证:全部改动文件 py_compile 通过;app.update / paths / startup / main_window / markdown_viewer 导入通过;isFullUpdateAsset(win32 exe→True, zip→False)、UPDATE_DIR 解析、用到的 FluentIcon.PLAY/UPDATEStateToolTip API 均已核实存在。
  • 真实端到端"下载 → 安装 → 退出"需真实发布包才能完整跑通,本机因单例锁被已运行实例占用未做完整 GUI 验证。
  • 复用下载引擎带来的自然结果:更新下载会遵守用户的全局限速设置。

XiaoYouChR and others added 30 commits July 2, 2026 19:44
- 引入 TaskError 和 StepError 类,替代原有的字符串错误表示
- 修改 TaskStep 的 setError 方法以接受 StepError 对象
- 更新所有功能模块中的错误处理逻辑,统一使用 TaskError 异常
- 在 UI 层添加错误消息的国际化支持
- 优化协程运行器中的错误字符串转换逻辑
- 为各功能模块添加专门的错误目录文件用于翻译
- 重构 BTTask 和其他任务类型的错误处理流程
- 统一各任务步骤的异常捕获和传递机制
- 修改任务状态更新逻辑以正确处理错误状态
- 添加对 BaseExceptionGroup 的特殊处理机制
- 在 m3u8_pack/cards.py 中导入 ToolTipFilter 并为 toggleButton 和 keyFileButton 安装事件过滤器
- 在 option_cards.py 中导入 ToolTipFilter 并为 resetButton 安装事件过滤器
- 提升工具提示的显示质量和用户体验
- 添加 notifyDiskSpaceInsufficient 函数用于显示磁盘空间不足通知
- 在 Android 和桌面平台上实现磁盘空间不足通知功能
- 将所有通知文本替换为多语言翻译支持
- 连接 taskService.diskSpaceInsufficient 信号到通知函数
- 移除原有的磁盘空间不足信息栏显示逻辑
- 更新通知标题和消息文本为多语言格式
- 在 resources.qrc 中添加 gd3.pt_BR.qm 文件引用
- 重构 sync_i18n_res.py 脚本,优化代码结构和路径处理
- 添加新的 translate_i18n.py 脚本用于自动翻译功能
- 集成 DeepSeek API 实现多语言自动翻译
- 支持并发翻译多个语言区域设置
- 添加翻译状态检查和错误处理机制
- 将 ToolTipFilter 的安装从条件判断中移至初始化方法
- 确保事件过滤器在组件初始化时统一安装
- 移除重复的事件过滤器安装代码
- 保持按钮工具提示功能的正常工作
- 将 RangeSettingCard 替换为 SpinBoxSettingCard 组件
- 移除单位标识符 KB 的硬编码显示
- 添加单步调整值为 64 的增量控制
- 优化设置项标题文字表述
- 保持原有的配置绑定和功能逻辑不变
- 新增 ADR 0004:Android 弹窗必须为子控件而非顶级窗口
- 新增 ADR 0005:Android 原生二进制文件通过 linker64 执行
- 新增 ADR 0006:Android 包名定义
- 添加 Android 构建说明文档,包含 Docker CI、模板补丁、自定义配方等内容
- HeaderSnapshot 增加 size 字段,从 onResponseStarted 缓存 Content-Length
- routeBrowserDownload fallback 链末尾补上 headerSnapshot.size
- 唤醒桌面端按钮改为始终显示
- 错误模板从英文改为中文,与 source-language zh_CN 一致
- 中文用户无需翻译即可看到正确消息
- binary 未安装提示引导用户去设置中安装
- app/startup.py 统一 loadEngine/startEngine/stopEngine
- 磁盘空间不足改为系统通知,不依赖窗口可见
- Android 保活绑定提前到 resumeSaved 前
- Android 浏览器扩展动态切换修复
- 通知和保活文案国际化
- Android 启动时检查更新
- 替换失效源(ghproxy.vip、gh.llkk.cc、ghproxy.homeboyc.cn)为实测可用的 gh-proxy.org、gh.ddlc.top、ghfast.top
- 探测改用 HEAD 请求 Release URL, 能准确筛出不支持下载的半残站
- 区分"不可用"(403/404)和"超时"(网络问题)两种失败状态
- 每个设置分组标题前加图标,主程序用 AppIcon,Pack 各自维护独立 QRC 图标
- 修复 fractional DPI 下边框/分隔线渲染不一致:圆角矩形用整数内缩,分隔线用 fillRect
- 统一浅色模式边框颜色为 rgba(0,0,0,24)
- OutputFolderCard 改用 FolderPicker 支持历史路径选择
- 补全缺失的 ToolTipFilter 安装
- 打包脚本排除 SVG/QRC 源文件
Python 3.3+ 命名空间包不需要 __init__.py 即可 import
- 恢复 GitHub Alert callout 渲染(NOTE/TIP/IMPORTANT/WARNING/CAUTION)
- 修复超链接颜色对比度过低的问题
- 修复发布对话框文件名被截断的问题
- 确认按钮触发应用内下载,更新流程函数集中到 app/update.py
- assetScore 识别 Android 平台和 .apk 格式
- TaskDraftDialog 在 Android 上禁用背景拖拽
- revealInFolder 改用 SHOpenFolderAndSelectItems 替代 explorer.exe /select,
- 调用前 SendInput 空鼠标事件绕过 Win11 前台限制 (PowerToys 方案)
- browser_service 移除无效的 requestForeground 调用
- 添加了 originalSaveFile 函数引用
- 实现了 resolveSaveFileName 函数来处理保存文件路径
- 将 resolveSaveFileName 绑定到 QFileDialog.getSaveFileName 方法
- 扩展了文件对话框补丁以支持保存文件操作
- 为PermissionBanner构造函数增加可选的text参数
- 当传入自定义文本时使用该文本,否则使用默认提示
- 保持原有的存储权限提示作为默认值
- 允许在不同场景下显示不同的权限提醒内容
- 维持现有的按钮和布局结构不变
- 移除所有函数中的 IS_ANDROID 检查逻辑
- 简化 nativeLibraryDir 函数返回类型定义
- 简化 isSystemDark 函数返回类型定义
- 移除 isStorageGranted 中的非 Android 平台默认返回值
- 移除 requestStoragePermission 中的非 Android 平台提前返回
- 移除 openFile 和 openFolder 中的平台检查逻辑
- 移除 sharedText 和 clearShare 中的平台检查逻辑
- headersForPage 返回 snapshot 全部白名单头,不再只过滤 cookie + user-agent
- 214 个硬编码中文字符串迁移到 chrome.i18n.getMessage
- 7 个语言的 _locales/messages.json(zh_CN, en_US, ja, ru, zh_TW, zh_HK, pt_BR)
- manifest 添加 default_locale,name/description 走 i18n
- crowdin-browser.yml 配置 + GitHub Actions 推拉工作流
- translate_i18n.py 支持 --browser 标志翻译浏览器扩展
- 位置优先视频上方右对齐,fallback 到下方、右侧、左侧
- z-index 提到 2147483647(CSS 最大值)
- IPC 改用独立隐藏窗口接收消息,不再依赖 MainWindow 存在
- MainWindow 移除 WM_USER_WAKE / WM_COPYDATA 处理
- 修复 nativeEvent 中 raiseWindow 未导入的 bug
- 在资源文件中添加了新的国际化语言包配置
- 为中文简体和英语美国版本添加了对应的qm文件映射
- 优化了资源配置结构以支持多语言切换功能
- Route fetched HTTP metadata through FeatureService
- Let BitTorrent parse fetched torrent data without HTTP ownership
- Fix file:// torrent paths and reject empty torrent files clearly
- 将 VERSION 常量从 "4.0.3" 更新为 "4.0.4"
- 保持其他配置常量不变
@XiaoYouChR

XiaoYouChR commented Jul 4, 2026

Copy link
Copy Markdown
Owner

现在增加了一个Task的生命周期管理者,这个代价有点大,处理起来有点麻烦。建议跟Runtime更新相关代码结合起来,比如M3U8运行时的更新,还有Ytdlp的更新等

XiaoYouChR and others added 9 commits July 5, 2026 15:31
- 重构时遗漏了 QT_SCALE_FACTOR / QT_ENABLE_HIGHDPI_SCALING 的设置
- 在 setupEnvironment 中 qconfig.load 之后、QApplication 之前写入
- CPython 3.11 的 mimetypes C 加速器遍历 HKCR 时不处理 PermissionError
- 禁用 C 加速器后���试走 Python 后备路径(已有逐键 except OSError)
- 兜底确保 _db 始终被初始化,避免后续所有 HTTP 解析全部失败
- 无 parent 的 RoundMenu 经 popup() 显示时不会调 adjustPosition
- 新增 TrayRoundMenu 在 showEvent 中修正尺寸并检测屏幕边界
- 覆写 matchPassive 限制为 /video/BVxxx 或 /video/avxxx 路径
- 避免复制 bilibili 首页/专栏/搜索等非视频链接时误弹下载窗口
- 其他 parser 的 passive 行为保持不变
新增 RuntimeUpdateService 实现所有外部依赖运行时的自动更新:
- 多实例并发模式:每个 Runtime 独立下载,互不干扰
- 手动 + 自动触发:支持用户点击和启动时状态检查
- 独立下载模式:StateToolTip 实时进度,不进任务队列
- 批量更新功能:设置页面一键更新所有运行时
- 自动清理机制:启动/退出时清理 update 临时目录

主要变更:
- app/services/runtime_update_service.py: 核心更新服务
- app/view/components/runtime_update_cards.py: UI 组件(批量更新卡片)
- app/view/components/setting_cards.py: RuntimeCard 集成新服务
- app/view/pages/setting_page.py: 添加运行时管理设置组
- app/services/feature_service.py: 新增 runtimes() 收集方法
- app/startup.py: 启动时检查 + update 目录清理
- app/config/paths.py: 新增 UPDATE_DIR 常量

设计参考 baf7c83..1ec147f 的应用更新优化模式,复用
BinaryRuntime.installTask() 避免重复实现,松耦合集成
RuntimeStatusService,架构清晰,易于扩展。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
修复 RuntimeCard 点击一键安装时的 NameError。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
问题:
1. 应用更新被错误地改为任务队列模式,导致用户体验下降
2. 批量更新卡片未使用 RuntimeUpdateService

修复:
- 新增 AppUpdateService:专门处理应用自身更新的独立下载
- 新增 AppUpdatePrompt:应用更新的 UI 提示管理(StateToolTip + InfoBar)
- 重构 app/update.py:
  - downloadBestAsset/downloadAsset 使用 AppUpdateService
  - 完整更新包(.exe/.msi/.dmg)走独立下载
  - 其他格式退化为任务队列
  - 保留旧方法名以兼容现有代码
- 新增 launchInstaller:跨平台启动安装程序
- 修复批量更新卡片:移除错误的 runtimeStatusService 导入
- 主程序集成 AppUpdatePrompt

现在行为与远程分支 baf7c83..1ec147f 的设计一致:
应用更新和运行时更新都使用独立下载 + StateToolTip 实时进度。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
问题:点击立即下载后,InfoBar 仍然显示,可能导致重复点击

修复:
- 点击下载按钮后立即关闭 InfoBar
- 改用 downloadBestAsset 替代已废弃的 addBestAssetTask

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bilixuesheng
bilixuesheng force-pushed the feat/update-download-install branch from 68c6eb0 to 87ad7cc Compare July 5, 2026 11:29
bilixuesheng and others added 3 commits July 5, 2026 19:33
_onTaskCreated 中错误地将 task.outputFolder 覆盖为 UPDATE_DIR,
而 installTask() 已经正确设置了运行时的安装目录,覆盖后:
1. 二进制文件被装到临时目录而非实际安装路径,安装失败
2. ExtractStep/InstallStep 路径计算错乱,进度无法正常上报

修复:移除 outputFolder 覆盖,直接使用 installTask() 返回的任务。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- RuntimeCard: replace StateToolTip with inline ProgressRing
  - IndeterminateProgressRing shown immediately on click (parsing phase)
  - Switches to ProgressRing with actual % once download begins
- RuntimeUpdateService: emit downloadStarted signal before parsing
  so UI responds instantly instead of after 1-2s delay
- BatchRuntimeUpdateCard: single StateToolTip showing aggregate
  progress across all parallel downloads (n/total, speed)
- AppUpdateService: emit downloadStarted before parsing
- AppUpdatePrompt: show StateToolTip on downloadStarted with
  'parsing...' text, eliminating the blank wait period

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Qt 在 document().setMarkdown() 后会将滚动条移至底部,
_refreshAlertStyle() 的内容插入操作也会触发同样行为。
在 setMarkdown() 末尾调用 moveCursor(Start) 滚回顶部。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@XiaoYouChR
XiaoYouChR force-pushed the main branch 7 times, most recently from f786fdb to 29af5d2 Compare July 11, 2026 19:59
@XiaoYouChR
XiaoYouChR force-pushed the main branch 6 times, most recently from 686fb4e to 234471c Compare July 22, 2026 08:42
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.

2 participants