# 订阅生命周期与订阅模式

本文说明主程序当前订阅领域的业务语义、字段所有权和使用方式，供维护订阅搜索、下载、进度刷新、重置和插件协同时参考。

## 核心模型

订阅是一条持续运行的媒体目标。它把用户想要的内容、搜索下载策略、已下载事实和完成进度保存在同一个订阅记录中。

订阅链路围绕四类状态工作：

| 状态类别 | 主要字段 | 含义 | 写入入口 |
| --- | --- | --- | --- |
| 目标范围 | `type`、`season`、`start_episode`、`total_episode` | 订阅需要覆盖的媒体范围。电影视为单个目标；电视剧按季和集范围处理。 | 新增订阅、订阅编辑、剧集刷新 |
| 下载事实 | `note`、`episode_priority` | 主程序或可信回填入口确认已经存在或已经下载的目标。`note` 表示存在事实，`episode_priority` 表示电视剧每集已知下载质量。 | 下载事实入口、backfill 入口、reset 入口 |
| 进度摘要 | `lack_episode`、`current_priority`、`completed_episode` | 面向搜索、完成判定和展示的派生进度。`completed_episode` 由响应层按当前事实计算。 | progress 刷新入口、响应构造 |
| 生命周期状态 | `state`、`last_update` | 订阅是否继续搜索，以及最近一次系统写入时间。 | 搜索下载流程、完成订阅、reset、状态更新 |

字段所有权应保持集中：下载事实入口写事实，progress 刷新入口写进度摘要，完成入口写完成状态，普通更新入口不应绕过这些语义直接改派生字段。

## 生命周期

| 阶段 | 触发来源 | 主程序行为 | 关键输出 |
| --- | --- | --- | --- |
| 创建订阅 | 用户、API、Agent、插件 | 记录媒体目标、订阅模式、季集范围、保存路径和下载策略。 | 初始订阅记录 |
| 搜索匹配 | 定时任务、手动搜索、RSS/站点刷新 | 按订阅目标和模式过滤候选资源，计算需要下载或升级的目标。 | 匹配候选、待下载上下文 |
| 下载选择 | 下载链路 | 将候选资源交给下载器，下载层可返回明确集数或完整覆盖确认。 | 下载上下文、剩余缺口 |
| 事实记录 | 下载完成或可信 backfill | 写入 `note`，电视剧同步写入或提升 `episode_priority`。 | 已下载事实快照 |
| 进度刷新 | 下载流程、backfill、剧集范围变化、显式刷新 | 普通电视剧按媒体库缺失结果刷新 `lack_episode`；洗版电视剧按目标范围和按集事实刷新 `lack_episode` 与 `current_priority`。 | 进度字段 |
| 完成判定 | 下载流程末端、强制完成 | 根据媒体类型和订阅模式判断是否完成，完成前允许完成检查事件否决。 | 订阅历史、完成状态 |
| 重置/回填 | 用户操作、插件、维护工具 | reset 清空事实和进度；backfill 写入调用方确认的外部存在事实。 | 新事实快照和进度摘要 |

## 订阅模式

MoviePilot 当前以媒体类型和洗版方式组合出常用订阅模式。普通电视剧订阅和分集洗版都是“按集目标”订阅：前者每集下载一次即可，后者允许同一集继续升级质量。全集洗版是整体资源质量目标，整包候选需要完整覆盖目标范围。

| 模式 | 适用媒体 | 目标口径 | 下载事实 | 进度口径 | 完成口径 | 典型用途 |
| --- | --- | --- | --- | --- | --- | --- |
| 电影普通订阅 | 电影 | 单个电影目标 | `note=[1]` 表示电影已下载。 | 不维护电视剧缺集进度。 | 有下载或强制完成即可完成。 | 追新电影、补电影库 |
| 电影洗版 | 电影 | 单个电影目标的整体质量 | `note=[1]` 表示电影已下载。 | `current_priority` 表示当前电影资源质量。 | `current_priority == 100` 或完成策略满足时完成。 | 用更高质量版本替换已有电影 |
| 普通电视剧订阅 | 电视剧 | 季内目标集范围 | `note` 记录已存在或已下载集；`episode_priority` 记录每集已知下载质量。 | `lack_episode` 来自媒体库缺失结果。 | 目标范围无缺集时完成。 | 追剧、补缺集 |
| 分集洗版 | 电视剧 | 季内目标集范围 | `note` 记录存在事实；`episode_priority` 记录每集质量。 | `lack_episode` 统计从未下载过任何版本的集；`current_priority` 是目标范围最低已知质量。 | 目标范围内每集达到顶级质量时完成洗版。 | 对单集逐步升级质量 |
| 全集洗版 | 电视剧 | 完整目标集范围的整体质量 | 整包下载确认完整覆盖后，按目标范围写 `note` 和 `episode_priority`。 | `current_priority` 作为整包候选整体质量门槛，并由按集事实刷新。 | 完整目标范围达到顶级质量时完成洗版。 | 用完整季包替换已有剧集 |

## 按集订阅与全集洗版

普通电视剧订阅和分集洗版共享同一个按集事实源：

- `note` 表示某一集已经存在或已经下载。
- `episode_priority` 表示某一集已知的下载质量，缺失 key 表示没有质量事实。
- `lack_episode` 只表达“还缺多少集没有任何版本”，不表达“还有多少集需要升级质量”。
- `current_priority` 对电视剧洗版是目标范围内最低已知质量摘要，缺失质量事实按 `0` 参与计算。

因此普通电视剧订阅和分集洗版可以互相转换。普通订阅下载产生的按集质量事实可被分集洗版继续使用；分集洗版产生的 `note` 也可被普通订阅继续作为已下载事实。

全集洗版关注完整目标范围的整体资源质量。整包候选必须完整覆盖 `[start_episode, total_episode]`，不是只覆盖当前缺口集。下载层确认完整覆盖后，主程序才可在资源缺少显式集数时按目标范围补写事实。

## 字段语义

| 字段 | 语义 | 关键约束 |
| --- | --- | --- |
| `note` | 已存在或已下载的电影项/电视剧集。 | 电影使用 `[1]`；电视剧使用集数列表。 |
| `episode_priority` | 电视剧按集质量事实，键为集数，值为下载优先级。 | 只升不降；普通订阅和洗版订阅都可以写入。 |
| `lack_episode` | 电视剧目标范围内仍缺少任意版本的集数。 | 普通订阅来自媒体库缺失结果；洗版订阅按 `note` 和 `episode_priority > 0` 计算。 |
| `current_priority` | 洗版整体质量摘要。 | 电影洗版直接维护；电视剧洗版按目标范围内最低已知质量刷新。 |
| `completed_episode` | 展示用完成集数。 | 响应层按当前事实计算，不作为主要事实源。 |
| `state` | 订阅运行状态。 | 完成、暂停、重置等入口按业务状态维护。 |

## 用户视角

| 想要做什么 | 推荐模式 | 行为说明 | 注意事项 |
| --- | --- | --- | --- |
| 新剧更新后自动下载 | 普通电视剧订阅 | 每集下载到任意版本即可，后续缺集继续追。 | 下载过的集会作为事实保留，后续可切到分集洗版。 |
| 已有剧集想逐集升级质量 | 分集洗版 | 每集按质量继续升级，达到顶级质量后不再下载该集。 | 低质量已下载集不计入缺集，但仍会作为待升级目标。 |
| 想用完整季包替换整季 | 全集洗版 | 优先寻找完整覆盖目标范围的整包资源，并按整体质量门槛判断是否下载。 | 整包必须覆盖目标集范围，不只是覆盖当前缺口。 |
| 新电影自动下载 | 电影普通订阅 | 下载到电影后完成订阅。 | 电影没有分集事实。 |
| 电影已有版本想升级 | 电影洗版 | 用 `current_priority` 表达当前电影质量，下载更高质量版本。 | 完成策略按电影整体质量判断。 |
| 手动入库或插件扫描到已有集 | backfill | 可信调用方写入已存在集；可选择是否提供 quality priority。 | `priority=None` 只表达存在事实；提供有效 priority 才表达质量事实。 |

## 维护边界

- 用户/API/Agent 的普通订阅编辑只负责目标和配置变更，不直接维护下载事实和进度摘要。
- 主程序下载链路产生的电视剧下载必须同时维护 `note` 和 `episode_priority`。
- backfill 入口只接收调用方确认的外部存在事实，不主动扫描媒体库。
- progress 刷新入口负责把当前事实转换为 `lack_episode` 和 TV 洗版 `current_priority`。
- 插件可以调用主程序公开入口补事实或刷新进度，不应复制主程序缺集、洗版完成或当前优先级计算规则。
