Skip to content

fix(downloader): 修复下载队列卡死、计数器泄漏和缺少重试机制等5个Bug#616

Open
junctiono369 wants to merge 4 commits into
maotoumao:masterfrom
junctiono369:master
Open

fix(downloader): 修复下载队列卡死、计数器泄漏和缺少重试机制等5个Bug#616
junctiono369 wants to merge 4 commits into
maotoumao:masterfrom
junctiono369:master

Conversation

@junctiono369
Copy link
Copy Markdown

修复以下Bug:

  • Bug 1: URL解析失败后队列永久卡死(缺少 downloadNextPendingTask() 调用)
  • Bug 2: 所有音质尝试失败时无日志记录
  • Bug 3: 目录创建失败导致 downloadingCount 泄漏
  • Bug 4: 下载失败无自动重试机制(3次重试,1s→3s→5s退避)
  • Bug 5: unlink 未捕获异常可能阻塞队列

根本原因分析

Bug 1 - 下载队列卡死

downloadNextPendingTask() 的 URL 解析 catch 块调用了 markTaskAsError() (downloadingCount--)后直接 return,没有调用 downloadNextPendingTask()。 当所有并发槽位的任务都因 URL 解析失败进入此路径时,downloadingCount 降为
0,但队列中仍有 Pending 状态的任务未被处理,队列永久卡死。

Bug 2 - 缺少错误日志

音质迭代循环中 catch {} 吞噬了 plugin.getMediaSource() 的所有错误,不 记录任何日志,调试困难。修复:所有音质均失败时通过 errorLog() 汇总日志。

Bug 3 - downloadingCount 泄漏

目录创建失败(mkdir)的 catch 块仅 emit 了 DownloadTaskError 事件,未调用 markTaskAsError()。markTaskAsStarted() 已将 downloadingCount +1,泄漏后 永久占用一个并发下载槽位,减少有效并发数。

Bug 4 - 缺少自动重试

下载失败直接进入 Error 状态,无重试机制。插件自带的单次重试(150ms)仅在
getMediaSource() 内部生效且对 NOT_RETRY 类型跳过。修复:添加 DownloadStatus. Retrying 状态,3次重试(1s→3s→5s退避),重试前将状态重置为 Pending 使
downloadNextPendingTask() 能重新拾取任务。

Bug 5 - unlink 未捕获异常

markTaskAsCompleted() 后的清理路径中,await unlink(cacheDownloadPath) 在 缓存文件不存在时抛出异常(如下载在 begin 回调之前就失败了)。异常未被捕获,
downloadNextPendingTask() 不会执行,队列卡死。

Ref #615

@junctiono369
Copy link
Copy Markdown
Author

@MonkeyCode-AI review 一下

@MonkeyCode-AI
Copy link
Copy Markdown

MonkeyCode-AI 正在分析任务...

修复以下Bug:
- Bug 1: URL解析失败后队列永久卡死(缺少 downloadNextPendingTask() 调用)
- Bug 2: 所有音质尝试失败时无日志记录
- Bug 3: 目录创建失败导致 downloadingCount 泄漏
- Bug 4: 下载失败无自动重试机制(3次重试,1s→3s→5s退避)
- Bug 5: unlink 未捕获异常可能阻塞队列

# 根本原因分析

## Bug 1 - 下载队列卡死
downloadNextPendingTask() 的 URL 解析 catch 块调用了 markTaskAsError()
(downloadingCount--)后直接 return,没有调用 downloadNextPendingTask()。
当所有并发槽位的任务都因 URL 解析失败进入此路径时,downloadingCount 降为
0,但队列中仍有 Pending 状态的任务未被处理,队列永久卡死。

## Bug 2 - 缺少错误日志
音质迭代循环中 catch {} 吞噬了 plugin.getMediaSource() 的所有错误,不
记录任何日志,调试困难。修复:所有音质均失败时通过 errorLog() 汇总日志。

## Bug 3 - downloadingCount 泄漏
目录创建失败(mkdir)的 catch 块仅 emit 了 DownloadTaskError 事件,未调用
markTaskAsError()。markTaskAsStarted() 已将 downloadingCount +1,泄漏后
永久占用一个并发下载槽位,减少有效并发数。

## Bug 4 - 缺少自动重试
下载失败直接进入 Error 状态,无重试机制。插件自带的单次重试(150ms)仅在
getMediaSource() 内部生效且对 NOT_RETRY 类型跳过。修复:添加 DownloadStatus.
Retrying 状态,3次重试(1s→3s→5s退避),重试前将状态重置为 Pending 使
downloadNextPendingTask() 能重新拾取任务。

## Bug 5 - unlink 未捕获异常
markTaskAsCompleted() 后的清理路径中,await unlink(cacheDownloadPath) 在
缓存文件不存在时抛出异常(如下载在 begin 回调之前就失败了)。异常未被捕获,
downloadNextPendingTask() 不会执行,队列卡死。

Ref maotoumao#615
将最大同时下载数目从固定的[1,3,5,7]选项改为数字输入框,支持输入1-16之间的任意数值。后端clamp同步更新为16。
将copyFile+unlink改为moveFile,省去一次文件复制和删除。

下载完成后立即markTaskAsCompleted + downloadNextPendingTask + 队列清理,

后续的moveFile/addMusic/patchMediaExtra作为不阻塞后处理执行。

下载失败时仍保留清理逻辑(unlink+downloadNextPendingTask)。
在 ubuntu-latest (x86_64) 上构建 Release APK,push 到 master 或手动触发。使用 debug.keystore 签名,产物保留 7 天。
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