媒体库整理 - 「二级分类路由」并入「媒体库整理」:转存前选目录、转存后整理,共用开关与设置 - 目录方式:二级分类 / 按媒体类型 / 不分类;按媒体类型的文件夹名可改 - 二级分类规则随插件发布(category.yaml,MoviePilot 式顺序匹配,增加剧集兜底),管理台只读 - 两种目录方式都以流程图展示,按媒体类型的文件夹名在图上修改 插件结构 - 每个插件一个目录;内置插件的默认设置与校验在插件的 settings.py,内核按目录调用 - 插件动作 ui_actions + 通用接口 /api/extensions/<名>/actions/<动作>,插件不依赖 Web 框架 - 前端插件专用界面移到 web/src/plugins/<名>/,按 config_type 登记 - storage_provider 统一校验,内核不再写死媒体库整理 管理台 - 插件页「运行概览」、统计页内部标签、运行状态徽标、标题与副标题「网盘转存助手」 - 设置页两列卡片不留空、下拉框直接选中默认项、统计页 echarts 按需引入(约 1 MB → 490 KB) 清理 - 移除启动迁移与 .env 导入、旧配置写法与 routing、旧凭证文件、旧去重键与旧中断状态 - 数据库结构迁移重置为单一基线;不认识的顶层配置项直接报错 - 删除未使用的代码与组件;修复测试警告;CI 固定 ubuntu-24.04 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
11 KiB
11 KiB
架构与可靠性设计
设计原则
- AI 提议,事实源校验,人工裁决,裁决结果反馈给 AI:AI 负责理解与提议——读消息、抽字段、给出存不存与存到哪的建议、在规则无法确定时给方案、把一句话偏好起草成规则;TMDB、分享清单与配置校验等事实源和确定性规则负责把关;拿不准的交给你裁决;你的批准、忽略与修正记成样本,回流到识别示例、偏好判断与自动审批建议里。调用 115、写目录、去重始终由确定性代码完成,转存后读回核验。
- 稳定内核 + 可插拔外围:主流程是固定骨架,能力都是挂在扩展点上的插件(见 插件开发指南)。
- 单一 Context 贯穿全程:一条消息带着
PipelineContext流经所有阶段,每次状态变更都持久化,可追溯、可恢复、可干预。 - 失败隔离:通知和事件先落库再独立投递;转存前置检查和整理失败会阻止任务完成,但不会拖垮其他任务。
一条消息的处理流程
flowchart LR
subgraph recv[接收]
direction TB
tg([Telegram 消息]) --> src[监听 Source] --> rcv[(落库 received)]
end
subgraph ident[识别与决策]
direction TB
bp[before_parse] --> parse[解析 Parser] --> ap["after_parse<br/>TMDB 补全 · 分享预读"] --> conf[确定性置信度] --> dec{决策 Decision}
end
subgraph store[转存与整理]
direction TB
bs["before_save<br/>选存放目录"] --> prep[准备清单] --> save[转存 Storage] --> verify[读回核验] --> as["after_save<br/>整理"]
end
recv --> ident
ident -- save --> store
ident -- ask --> pend[[pending_approval]]
pend -- 批准 --> store
pend -- 忽略 --> rejected([rejected])
ident -- skip --> skipped([skipped])
store --> done([done])
任务状态机
stateDiagram-v2
direction LR
[*] --> received
received --> parsed
parsed --> pending_approval: 需要确认
parsed --> queued: 自动放行
parsed --> skipped: 不收
pending_approval --> queued: 批准
pending_approval --> rejected: 忽略
queued --> saving
queued --> skipped: 内容已入库
saving --> stored: 读回核验通过
saving --> need_manual: 结果不明
stored --> organizing
organizing --> done
organizing --> need_manual: 整理失败
need_manual --> queued: 核验转存 / 重新整理 / 自动复查
received --> failed: 解析失败
queued --> failed: 准备或转存失败
failed --> received: 重试
failed --> queued: 重试
failed --> stored: 重试
done --> [*]
进入 failed 的任务从记录的阶段(retry_from)重试:临时错误自动退避重试(not_before,不占并发槽),分享失效、校验不通过这类重试也没用的只能人工重试。
- 审批态落库:待确认任务持久化为
pending_approval,按钮 callback 只带任务 ID,进程重启后仍可点。 - 三层去重:消息级(来源 + 消息 ID)、资源级(存储 Provider + 目标 CID + 分享码)、内容级(存储 Provider + 目标 CID + 标题 + 季 + 集)。剧集按集去重,同一分享链接追更新增的集仍会转存。
- 确定性校验失败(集号无法识别、多季合集中有文件不带季号、同一集有多个文件、电影有多个正片候选)时先让 AI 给方案(
ai.assist),方案经同一套规则校验通过才继续;否则在转存前停止,任务标记为不自动重试,修正后人工重试。
AI 与规则的分工
| 能力 | AI 做什么 | 谁来把关 |
|---|---|---|
识别(providers/parser.py) |
判类、抽字段与版本信息;参考同一来源最近的人工修正(ai.examples) |
strict schema + 本地校验;分享码只由代码提取;TMDB 回填规范元数据;置信度由可核对信号计算 |
偏好判断(decision: llm) |
按一句话偏好与近期批准 / 忽略记录决定存不存、存到哪 | 只能否决或转人工,不能越过过滤规则、置信度与人工确认;选出的目录与手动指定同等对待 |
订阅放行(decision: subscription) |
—(订阅本身可由 AI 把一句话对应到 TMDB 条目,你确认后写入) | 只认 TMDB 匹配的同一条目同一季;消息与读取分享都不能确认只涉及这一季(如跨多季的合集)时不放行,消息没写季号时把季记为订阅的那一季、转存只取该季;置信度、过滤规则、分享清单与去重照常生效 |
转人工前兜底(core/assist.py) |
规则无法确定转存文件时给出季集映射(含多季合集里不带季号的文件)、多版本取舍、正片选择,以及消息只写了集数时选哪一季 | 交回同一套选集规则校验:不能与文件名冲突、不能丢掉某集全部文件、不能保留更差的版本、分段电影不能只选一段;不过仍转人工 |
一句话生成规则(core/compiler.py) |
把偏好起草成过滤、分类与版本偏好 | 按正式配置校验(不过时带错误重写一次),列出改动由你确认后写入;运行时只按规则判断 |
审批建议(core/inbox.py) |
把事实写成一句理由 | 事实(已入库的集、版本对比、订阅、识别可靠性)与建议动作都由规则决定 |
| 新来源接入、来源质量 | 起草来源规则;读统计与被拒样例给出调整建议 | 草案按正式配置校验并用样例试跑;建议的屏蔽词在批准过的消息里出现就丢弃;改动由你确认 |
| 失败解释、日报、控制 Bot 对话 | 把原因、统计写成话;理解一句话指令并调用查询工具 | 建议操作由规则决定;数字由代码统计;写操作只生成待确认计划,点「执行」后才调用服务层 |
人工裁决(批准、忽略、修正)统一记在 feedback 表(正文去掉链接与访问码),用于识别示例、偏好参考、按来源的通过率与「改为自动转存」建议,以及本地评测(python -m reelay.eval,样本只留在本地 data/eval/)。每次 AI 请求记入 llm_calls(用途、模型、耗时、token 与是否通过校验,保留 90 天),管理台「统计」可查看。所有 AI 调用失败时都按规则降级或转人工,不阻断主流程。
识别
- 调用 LLM 前先做确定性预过滤(无 115 链接、黑名单、缺 must_have),相同正文命中缓存,瞬时错误退避重试,非法输出重试一次;仍失败则降级 regex 并强制人工确认。
- 分享码与访问码只由确定性代码从正文和超链接中提取,不采信 LLM。
- 置信度由标题/类型/年份/TMDB 匹配等可核对信号计算(
reelay/core/confidence.py),LLM 自报值仅保存在raw_fields.parser_confidence。 - 发布版本信息(字幕、音轨、HDR、片源、编码、打包形式)由 LLM 一并抽取,规则从正文补上 LLM 漏掉的明确标记(
reelay/core/release.py)。 - TMDB 按标题相似度与年份打分,最佳候选不可信时不改标题并转人工。
转存与恢复边界
StorageProvider 分为 prepare(列出分享、确定文件清单、建立独占暂存目录)、save(发送一次转存)、reconcile(只读核验):
- 分享清单是集数的事实来源:剔除样片/预告/花絮,按文件名识别季集,排除目标目录中已入库的集。规则无法确定时先让 AI 给方案,方案经同一套规则校验后写入清单(每个文件带季集),整理沿用清单里的季集。
- 文件清单和
receiving检查点必须在调用 115 写接口之前提交。115 没有可用的请求幂等键,发送前后中断时无法确认是否已执行,所以receiving恢复后只核验、绝不重发;空目录、部分落地、内容不符都进入need_manual,由管理台"核验转存"重新读回;主进程也会在后台自动只读复查(5 分钟起、间隔加倍、最长 1 小时,24 小时后停止),覆盖 115 落地慢的情况,复查期间不发提醒,停止复查时才通知。 - 落地文件按名称、数量、大小与可用 SHA1 完整核验,分别记录源文件与落地文件 ID。转存回执、去重记录与释放资源占位在同一事务内提交,之后整理可独立恢复。
- 资源占位(分享与集)在准备阶段获取;集数确定后如需占用的集正被其他任务持有,会释放全部占位再排队,避免互相等待;发送前重新核对其他任务是否已入库同一内容。
- 整理先持久化计划,再按文件 ID 执行建目录/改名/移动并读回核验;中断后按文件实际位置续跑。同名冲突按
plugins.organizer.conflict处理,删除动作先记入扩展状态,重放不会重复删除。 - SQLite 使用 WAL + FULL 同步;数据库结构以
PRAGMA user_version版本化迁移,版本高于程序时拒绝打开。
通知、事件与告警
- 通知与事件使用事务 Outbox:与任务状态在同一事务内写入,投递器独立重试(单次 30 秒超时,最多 8 次,支持 Telegram FloodWait),至少一次语义。已处理的审批和旧轮次通知不再推送。
- 系统通知(故障告警与恢复、审批汇总、日报)同样走 Outbox,进程重启不丢。告警按类别去重:状态由正常变为异常时通知一次,恢复时发送恢复通知。
- 115 cookie 缺失或失效时暂停转存与整理的领取(解析照常),任务回到原阶段等待,不消耗重试次数,凭证恢复后自动继续。
- 消息源断开超过阈值告警;重连后按每个来源已接收的最大消息 ID 补拉停机期间的消息。
配置与快照
任务接收时保存审批、过滤、转发、路由、命名和扩展开关的配置快照:配置更新只影响新任务;"重新解析"使用当前策略,"重新整理"使用当前命名与扩展配置。目录别名在尚无转存检查点时解析为当前 CID,有检查点后绑定原 Provider、CID、清单与文件 ID。
进程模型
python -m reelay.main:消息源 + Worker + 控制 Bot,单实例(数据库旁的文件锁阻止两个 Worker 消费同一数据库)。python -m reelay.web:管理 API 与前端,只读写任务状态,不启动 Worker。python -m reelay.mcp_server:MCP 的 stdio 接入,与 Web 相同的服务层;Web 进程也在/mcp提供同一组工具(Streamable HTTP,凭 MCP 访问令牌)。工具的定义、参数校验与实现在reelay/agent_tools.py,控制 Bot 的对话助手也从这里取工具,区别只在写操作何时执行:MCP 直接执行(客户端按工具标注确认),对话助手先列出来,用户点「执行」后才执行。
三者共享同一个 SQLite 数据库与配置文件;Web/MCP 修改配置后,Worker 约 1 秒内采用。