任务系统
状态机规则与调度机制。
架构总览描述了任务的完整生命周期。这一页深入状态机的内部运作。
源码:app/services/task_service.py、app/models/task.py。
状态派生
任务的状态不是直接设置的值,而是从所有步骤的状态聚合而来。
| 转换 | 触发 |
|---|---|
| → 等待 | 入队、恢复保存的任务、重新开始 |
| 等待 → 运行 | 调度器分配到异步线程 |
| 运行 → 完成 | 所有步骤执行完毕 |
| 运行 → 失败 | 任意步骤永久错误 |
| 运行 → 已暂停 | 用户暂停 |
| 已暂停 / 失败 → 等待 | 用户重新开始 |
| 完成 → 等待 | 已完成任务勾选了新的未完成文件 |
派生规则(按优先级):任一步骤失败 → 失败。全部完成 → 完成。任一运行 → 运行。全部已暂停 → 已暂停。其余 → 等待。
设置任务状态时,先把状态推到所有未完成的步骤,再从步骤重新派生。直接写任务的状态字段无效,下一次步骤状态变更时会被覆盖。
一个三步任务(下载视频 → 下载音频 → 混流),第二步失败时:第一步已完成(保持完成),第二步失败,第三步还在等待。派生结果 → 失败。用户重新开始时,第一步跳过,第二步进度清零重新执行。
步骤迭代
执行时按步骤序号顺序迭代。迭代器在取下一个步骤前重新检查任务状态。如果上一个步骤失败导致任务状态变为失败,迭代立即终止。这是单一错误边界:任何步骤的失败都阻止后续步骤执行。
已完成和未勾选的步骤跳过,只执行处于等待状态的步骤。
取消(暂停)与失败的异常路径不同。取消通过 CancelledError 传播,步骤内部捕获后标记为已暂停;失败通过 TaskError 传播,步骤内部捕获后标记为失败并记录错误信息。两种异常最终都会中止步骤迭代,但对后续恢复行为的影响不同:已暂停的步骤从断点继续,失败的步骤从头执行。
调度
等待队列是一个有序列表。调度器循环检查:运行中的任务数 < 最大并发数时,从队列头部取出任务提交到异步线程。
用户调整最大并发数时,如果运行中的任务数超出新上限,多余的任务会被取消回到等待队列,只取消支持暂停的步骤。不支持暂停的步骤(如 FFmpeg 混流)会继续运行直到完成。
操作链路
所有变更操作的核心模式:取消运行 → 回调 → 后续动作。回调时序取决于任务是否在运行,这是异步桥接的必然结果,详见架构总览。
| 操作 | 取消后做什么 |
|---|---|
| 重新下载 | 删除输出文件和进度文件 → 重置所有步骤为等待 → 重新调度 |
| 编辑 | 可选删除文件 → 替换参数(URL / 步骤 / 文件大小,任务 ID 不变)→ 应用新选项 → 重新调度 |
| 勾选变更 | 更新勾选列表 → 如果已完成任务新增了未完成文件:重新调度 |
| 删除 | 可选删除输出文件 → 从内存和持久化中移除 |