# [4.0.0-beta.1] - 2026-10-08 4.0 是插件化重构的大版本(设计见 `docs/plans/2026-10-08-plugin-architecture-4.0-design.md`)。本节随各阶段合入持续更新。 - **新增(插件运行时)**:新增插件 SDK `ab_sdk`,包括 `Plugin` 基类、`@hook` / `@provider` / `@subscribe`、`PluginContext`,以及供插件作者写单元测试的 `ab_sdk.testing`;新增宿主运行时 `module/plugin`,负责清单解析、内置 / 本地目录 / pip entry point 三种来源的发现与加载、扩展点注册表、钩子超时与熔断、事件总线和插件私有 KV 存储(数据库迁移 v25)。新增 `plugins` 配置段和 `GET /api/v1/plugins`。本地与 pip 插件未签名,需开启 `plugins.allow_unsigned` 才会加载 - **新增(插件扩展点)**:插件可以提供下载器、通知渠道、LLM 解析提供商、搜索站点和定时任务(`ab_sdk.points`)。内置的 qB / aria2 和各通知渠道走同一套扩展注册表。设置页新增「插件」卡片,可查看状态、启停插件,并通过插件配置模型自动生成的表单修改配置;`secret_field` 声明的秘密字段在读取接口中一律掩码。下载器类型和通知渠道下拉框会列出插件提供的选项。新增插件开发文档(开发者指南 → 插件开发) - **新增(ingest 扩展点)**:RSS 下载流程开放 `torrent.filter`(种子过滤)、`title.parsed`(修正解析结果)、`torrent.adding`(修改保存路径 / 分类 / 标签)、`http.request`(为私有站点添加 Cookie 等请求头)四个钩子,以及 `metadata_provider` 元数据源扩展点:内置 mikan / tmdb 以 `core` 身份登记,插件可按 RSS 订阅的「解析器」取值提供新的元数据源,添加订阅时的解析器下拉框会列出插件选项。插件 SDK 升至 0.3.0,新增 `ab_sdk.ingest`。未安装插件时行为不变 - **新增(种子过滤)**:新增默认启用的内置插件「种子过滤」(`ingest-filters`)。在 设置 → 插件 中填写「包含过滤」后,只下载名称匹配任一表达式的种子(对所有订阅生效);留空时不过滤 - **新增(事件与外部接口)**:系统事件(RSS 订阅异常、种子添加失败、下载器不可用、新版本等)在写入通知中心后发布到插件事件总线,插件可用 `@subscribe("rss_failure")` 等订阅;事件类移入 `ab_sdk.events`(`SystemEvent` 基类,新增 `i18n()` 返回前端 i18n key 与参数)。插件可以提供 REST 路由(挂载于 `/api/v1/plugins//`,强制登录鉴权,随插件启停出现和消失)、MCP 工具(工具名 `<插件 id>__`)与 MCP 资源(`autobangumi://plugins/<插件 id>/`),并可通过 `message_template` 钩子按事件类型和渠道改写系统通知的标题与正文。`ab_sdk` 升至 0.3.0 - **优化(通知中心推送)**:SSE 的 `notification` 帧改为订阅事件总线,通知中心有新消息、已读或删除时立即推送(原先每 3 秒比较一次修订号),推送格式不变 - **修复(搜索)**:`GET /api/v1/search/provider` 读取的是导入时的站点快照,新保存的搜索站点要重启后才会出现在列表中;现在实时读取 - **修复(MCP)**:MCP 客户端每次断开 SSE 连接后,日志都会出现 `AssertionError: Unexpected message http.response.start`(3.x 起存在)。原因是 `/sse` 端点在 SSE 响应结束后又返回了一个空 `Response`,现在改为裸 ASGI 端点 - **新增(整理流水线)**:重命名方式改为 `rename_strategy` 扩展点:`pn` / `advance` / `template` 由默认启用的内置插件「重命名」(`rename`)提供,`none` 由宿主提供;`pn` / `advance` 的输出与此前逐字一致。字幕不再使用 `subtitle_*` 平行方式,同一方式按文件类型生成。`template` 用 Jinja2 沙箱模板自定义文件名(默认模板与 `pn` 相同),保存时试渲染、不合法直接拒绝;运行时渲染失败的文件保留原名并发送「文件未重命名」通知,不会退回 `pn`。文件分类(`media_files`)与版本冲突策略(`conflict_policy`)改为宿主 Provider。重命名后向事件总线发布 `file.renamed` / `torrent.organized`(`ab_sdk.events`)。`ab_sdk` 升至 0.4.0 - **新增(硬链接)**:新增默认停用的内置插件「硬链接到媒体库」(`hardlink`):种子整理完成后把正片与字幕链接到媒体库目录,按下载器实例做路径映射(`path_map`),跨文件系统时默认复制;不覆盖不是它创建的文件,版本升级后替换自己创建的链接;已有文件经 `POST /api/v1/plugins/hardlink/backfill` 按需补链 - **新增(媒体库刷新)**:新增内置插件「媒体库刷新」(`media-server-refresh`):配置 Jellyfin / Emby / Plex 地址与 API Key 后,合并一段时间内整理完成的种子,请求刷新媒体库;未配置时不做任何事 - **重构(整理流水线)**:`manager/renamer.py` 拆为编排(`renamer.py`)与 revision 替换事务(`revision_saga.py`);删除仅测试使用的 `Renamer.rename_file` / `_lookup_offsets`、无调用方的 `release_replacement_lease`、`match_by_save_path`、`search_by_qb_hash`,以及在重命名链路中传递却从未使用的 `season_offset` 参数 - **修复(插件配置)**:插件配置模型的校验器(`field_validator`)报错时,保存接口返回 500 而不是 422(错误详情中的异常对象无法序列化) - **破坏性变更(配置,多下载器)**:下载器改为 `plugins.instances` 中的实例,`plugins.slots.downloader` 指向默认实例;重命名方式与版本冲突策略移到 `plugins.slots.rename_strategy` / `conflict_policy`(另有 `media_files`),插件提供的实现也可以通过 slots 选用。升级后第一次启动时,3.3 的 `downloader`、`bangumi_manage.rename_method`、`bangumi_manage.revision_conflict_policy` 自动迁移,原文件备份为 `config.json.v3.bak`(已有备份时不覆盖,另存为 `config.json.v3.bak.1` 等);迁移失败时从备份恢复配置并拒绝启动,日志写明出错的字段。`AB_DOWNLOADER_*`、`AB_DOWNLOAD_PATH`、`AB_METHOD`、`AB_REVISION_CONFLICT_POLICY` 环境变量照常生效。番剧、电影、RSS 订阅与种子新增 `downloader_id` 列(数据库迁移 v26):存量种子归属 `default`,规则与订阅为空(跟随默认实例)。`ab_sdk` 升至 0.5.0:`ConflictRequest` 删除 `configured`,宿主的冲突策略改为 `hold` / `replace` 两个 Provider - **新增(多下载器)**:设置 → 下载器设置 可以添加多个下载器实例(qBittorrent、aria2 或插件提供的下载器)并指定默认实例。规则与 RSS 订阅可以选择下载器,留空时用默认实例;由订阅新建的规则继承订阅的选择。每个种子记录它所在的下载器,重命名、删除都在该下载器上进行,规则改用另一个下载器后已有种子留在原处,新种子保存到该下载器的下载目录(默认下载器切换或下载器被删除时也是如此)。重命名逐个下载器运行,连不上的下载器被跳过,并在第一次连不上时通知(`DownloaderUnavailableEvent` 新增 `instance_id`,按实例去重)。下载器页与种子列表标出种子所在的下载器;`GET /api/v1/downloader/torrents` 汇总所有实例,暂停 / 恢复 / 删除 / 打标接口新增 `downloader_id`,新增 `GET /api/v1/downloader/instances` - **新增(插件开发工具)**:`ab_sdk` 可单独打包为 `autobangumi-sdk` 轮子(`uv build --wheel backend/sdk`),带 `ab-plugin` 命令行:`new` 生成插件骨架与契约测试,`validate` 校验清单和 SDK 版本范围,`pack` 打包为可安装的 zip,`dev` 把插件目录链接到 `config/plugins/local/` 并开启 `plugins.dev_mode`。开启 `dev_mode` 后,修改本地插件的文件会自动重新加载该插件(包括上次加载失败的)。`ab_sdk.testing` 新增 `DownloaderContract`、`RenameStrategyContract`、`NotifierContract`、`SearchSiteContract` 契约测试套件,内置的下载器、重命名方式、通知渠道和默认搜索站点都通过它们 - **新增(签名插件目录)**:LLM 插件的安装器泛化为通用的签名目录安装器:`GET /api/v1/plugins/catalog` 列出目录(GitHub release `plugins`)中的插件,`POST /api/v1/plugins/{id}/install` 验签(目录、sha256、ed25519)后安装并启用,`DELETE /api/v1/plugins/{id}` 卸载。已安装的插件来源为 `catalog`,视为已签名。插件 id `local` 改为保留 - **破坏性变更(通知渠道)**:Bark 不再读取旧字段 `token`(改用 `device_key`),WeCom 不再读取 `chat_id`(改用 `webhook_url`)。升级后第一次启动时自动把旧字段迁移到新字段,原文件备份为 `config.json.v3.bak` - **新增(插件文档与示例)**:插件开发文档拆为总览、核心概念、配置表单、事件、前端挂载点、命令行、签名与分发、内置插件、示例和每个扩展点一页(中 / 英 / 日)。`examples/plugins/` 新增 `webhook-on-event`、`custom-rss-site`、`template-rename`、`nfo-writer`、`ntfy-notifier`,CI 逐个校验并运行它们的测试。新增插件作者 skill(`skills/autobangumi-plugin/`) - **新增(发布)**:beta 与稳定版的 GitHub Release 附带 `autobangumi-sdk` 轮子和 `autobangumi-plugin-skill-<版本>.zip` - **修复(插件开发工具)**:插件目录里的 `.venv`、`node_modules` 等工具链目录不再被当作原生扩展(`ab-plugin validate` / `pack` / `dev` 和宿主加载此前会报「含原生扩展」),`dev_mode` 也不再监听它们 - **修复(配置迁移)**:配置里有 Bark 或 WeCom 渠道时,每次启动都会新增一份 `config.json.v3.bak.N` 并改写配置;现在只迁移带值的旧字段 - **修复(在线更新)**:在线更新包带上 `ab_sdk`,4.0 镜像启动时与 `module` 一起替换;更新包要求镜像不低于 4.0.0-beta.1,已安装 3.3 的用户在 beta 通道不会应用 4.x 更新包(缺少 `ab_sdk` 与新依赖,应用后无法启动),需要拉取 4.0 镜像 - **修复(多下载器)**:删除规则并删除文件时,若某个下载器不可用,规则与种子记录会保留并提示稍后重试;此前规则和种子记录已先删除,该下载器上的种子之后无法再删 - **修复(多下载器)**:aria2 任务与番剧的关联按下载器实例区分,两个 aria2 实例分配到相同 gid 时不再互相覆盖,同一种子也可以分别加入两个实例;插件下载器的工厂参数 `DownloaderConnection` 新增 `instance_id` - **修复(回退 3.3)**:4.x 改把运行版本写入 `config/version_v4.info`,`config/version.info` 保留 3.x 的最后记录。此前 3.3 读到 `4.0.0` 只比较 minor,会当作 3.0 数据运行 3.0→3.1 迁移并重建数据库。首次以 4.x 启动 3.x 数据时还会把数据库备份为 `data/data.db.v3.bak`,回退时与 `config.json.v3.bak` 一起还原 - **新增(插件目录界面)**:设置 → 插件 新增「插件目录」:浏览签名目录中的插件并安装、更新;目录安装的插件可在卡片上卸载(内置、本地、pip 与 LLM 提供商插件不提供卸载)。插件卡片标出已签名或未签名。目录不可达时在区内显示原因,不弹全局提示 - **修复(插件设置)**:经签名目录安装的插件在插件卡片上的来源显示为未翻译的 `config.plugins_set.source_catalog` - **修复(插件安装)**:`DELETE /api/v1/plugins/{id}` 不再能卸载 LLM 提供商插件;安装通用插件后,LLM 插件列举不再反复输出 `Skipping broken plugin` 警告 - **修复(插件安装)**:LLM 提供商插件的卸载也校验插件 id,并且只删除 LLM 安装器装入的目录:`..`、`local` 与目录安装的通用插件会被拒绝;卸载未安装的 id 现在返回「Plugin not installed」(400),此前返回成功。`build_plugin_catalog.py` 默认 `min_ab_version` 改为 `4.0.0-beta.1`,此前的 `4.0.0` 会让 beta 版拒绝安装目录中的插件 - **修复(插件运行时)**:清单不是 UTF-8 或 pip 插件导入失败时,该插件显示为加载失败,不再中断全部插件的发现;`setup()` 抛错或超时后调用 `teardown` 释放已占用的资源;事件订阅者成功一次即清零熔断计数,只有连续失败才熔断;停用一个加载失败的插件后状态显示为「已停用」,重新启用时会再次尝试加载;插件重载后,旧实例的熔断不再停用新实例 - **修复(设置向导)**:向导中填写的 Bark / WeCom 凭据写入渠道实际读取的 `device_key` / `webhook_url`,此前测试通知与保存后的渠道都拿不到凭据 - **修复(升级路径)**:首次启动时也检查 2.x 的 `data/data.json`;没有 `version.info` 的 3.0 数据库会被识别并要求先升级到 3.1.x;拒绝启动的提示写明要先把 `config.json.v3.bak` 还原为 `config.json` 再启动旧版本。配置迁移失败时把备份移回原文件,反复重启不再堆积 `config.json.v3.bak.N` - **修复(多下载器)**:修改规则的保存路径时,若某个下载器不可用,不移动任何种子也不修改规则,提示稍后重试;此前可用实例上的种子已移动而规则路径未更新。下载器页的批量暂停 / 恢复 / 删除按实例分别执行并汇总失败,同一 hash 在两个实例上时只操作选中的那一个;只配置了插件下载器时下载器页不再显示为未配置 - **修复(整理)**:插件提供的版本冲突策略或文件分类出错时退回宿主实现并计入熔断,不再中断整次重命名;重命名方式的插件未加载而按 `none` 处理时不发布 `torrent.organized`;已重命名的种子在每次启动后补发一次 `torrent.organized`,此前错过该事件的种子不会再被硬链接或触发媒体库刷新(开启硬链接时,重启后会对仍在下载器中的已整理种子补链一次) - **修复(硬链接)**:用户从媒体库删除的硬链接文件不再被重新创建(`backfill` 仍会恢复);媒体库刷新在硬链接放好文件后再刷新一次,刷新请求进行中到达的整理事件不再丢失 - **修复(插件配置)**:秘密字段的标记写在 `Optional` / 嵌套模型引用外层或 `list` / `dict` 容器上时,读取接口此前返回明文,现在掩码;`/config/get` 掩码插件通知渠道的自定义字段,保存时按渠道还原 - **修复(插件设置)**:在插件卡片上切换「允许未签名插件」后,点全局保存不再把它改回旧值;保存或启停一个插件不再清掉其他卡片未保存的输入;整数枚举的下拉框保存为数字而不是字符串 - **破坏性变更(升级路径)**:只支持从 3.3.x 升级。检测到更早版本的数据(`config/version.info` 低于 3.3,或残留 2.x 的 `data/data.json`)时拒绝启动,并提示先升级到最新 3.3.x 启动一次完成迁移 - **破坏性变更(API)**:移除 3.2 兼容的 GET 控制端点(`/api/v1/restart`、`/start`、`/stop`、`/shutdown`)和 `GET /api/v1/auth/refresh_token`,请改用 POST - **破坏性变更(配置)**:移除旧版 `experimental_openai` 配置节和通知的单 provider 旧字段(`type` / `token` / `chat_id`),3.3 已把它们迁移到 `llm` 与 `notification.providers`;移除废弃的 `normal` 重命名方式,已有配置自动改为语义相同的 `none` - **清理**:删除 3.0→3.1、3.1→3.2 跨版本迁移、旧 JWT 与内存会话实现,以及约 70 处无调用方的函数、方法和模型;CI 新增 vulture 死代码检查 - **修复(版本记录)**:`version.info` 原先只比较次版本号,跨主版本升级(如 3.3 → 4.0)不会被记录;现按完整语义化版本比较 # [3.3.6] - 2026-09-19 3.3.6 是问题修复版本,修复 3.3.5 发布后报告的 RSS 匹配、网络、解析与部署问题。 - **修复(#1103/#1114,RSS 匹配)**:解析器重组的 `title_raw`(剔除标题中间的集数、拼接 ` / ` 分隔的别名)不是种子名的子串,导致新剧集无法匹配、被归入孤儿种子;现在与种子自身重组出的标题完全相等也算命中,已有数据无需迁移 - **修复(#1105,订阅)**:搜索类 RSS 同时返回多季资源时,订阅/收集整订阅下载会把其他季的种子按本季下载并重命名,互相覆盖;现在按季度/类型过滤,与 RSS 刷新的匹配规则一致 - **修复(#1104,网络)**:HTTP 代理下 CONNECT 成功但 TLS 握手失败时,httpcore 泄漏隧道连接,连接池耗尽后持续 `PoolTimeout` 直至重启;现在遇到 `PoolTimeout` 时为后续请求换用新连接池,不中断进行中的请求 - **修复(#1108,解析器)**:Unicode 罗马数字(`Ⅱ` 等)识别为季度,去除包裹中文标题的全角破折号,`Clevatess Ⅱ-魔獸之王與虛假的勇者傳承-` 解析为第 2 季并可匹配 TMDB - **修复(#1106,Docker)**:健康检查改为读取配置的 WebUI 端口,依次探测 IPv4 与 IPv6 回环地址,不再因 `localhost` 解析为 `::1` 或修改端口而被标记为 unhealthy - **修复(#1117,日志)**:RSS/重命名间隔配置为 0 时后台循环空转刷屏,现在最小间隔为 1 秒;部署文档中的 docker-compose 与 `docker run` 示例增加容器日志大小上限(10 MB × 3) # [3.3.5] - 2026-07-26 3.3.5 是问题修复版本,包含五个订阅、通知与配置相关的修复。 - **修复(#1095,RSS)**:删除番剧后重新订阅同一 RSS 时,自动重新启用被孤儿停用机制禁用的订阅,番剧不再静默断更 - **修复(#1094,通知)**:Telegram 海报默认改为发送源 URL(由 Telegram 服务端拉图),失败时降级为本地上传、再降级为纯文本消息;下载完成通知不再因海报发送失败而整条丢失,服务端拒绝(4xx/5xx)与连接失败的日志分开记录并附带响应内容 - **修复(#1092,解析器)**:classic 引擎与 Preview 准入策略对齐,接纳字幕组未标记特别篇/剧场版且无集数、但带发布证据(组名/分辨率/片源)的单发资源,按整理集处理;纯文字弱标题仍被拒绝 - **修复(数据库)**:「重置所有规则」先清理种子记录与 aria2 映射的外键引用再删除番剧,不再因 `FOREIGN KEY constraint failed` 整体回滚;重置失败残留的去重记录导致番剧补全跳过已有集数的问题随之解决 - **修复(配置)**:未设置的密码/密钥不再在界面上显示为 `********` 幻影掩码;自动清洗 3.2.3/3.2.4 版本窗口期间被字面写入配置的掩码哨兵(该窗口只掩码不恢复,导致代理/下载器认证失败),清洗时告警提示重新输入 # [3.3.4] - 2026-07-13 3.3.4 正式版汇总 beta.1–beta.2。经典解析器仍为默认;通用解析器 Preview 与修订版自动替换均需手动启用。 - **新增(#1074,认证)**:引入持久化多用户会话、首次管理员初始化、用户管理,以及按 `api` / `mcp` scope 隔离且仅展示一次明文的哈希令牌;密码变更、禁用用户和退出登录会撤销相关会话,浏览器刷新仅续期 HttpOnly Cookie - **新增(Parser Preview)**:解析设置可在稳定的经典解析器与通用解析器 Preview 间切换;Preview 支持剧集、OVA/特别篇、剧场版、范围和混合合集,两种模式不会自动互相回退 - **修复(#1078)**:更高修订版与既有文件冲突时,默认 `hold` 记录并通知冲突;可选 `replace` 在严格匹配番剧、媒体类型、季集、字幕组和分辨率后,通过 qBittorrent/aria2 的可恢复三阶段流程替换旧版。自动替换首版仅处理双方均为单文件的任务 - **修复(#1077)**:日历不再显示已归档番剧 - **修复(#1072/#1075,解析)**:`06v2` 等修正版集数不再误触发 offset review;TMDB 电视与电影查询遵循配置语言;剧场版标题中的有效 `Movie` 和中日韩电影词不再被误删 - **CI/发布**:新增隔离的运行时、Chromium/WebKit 与真实 qBittorrent E2E lanes;release 分类仅依据 event/ref/tag,正式版 tag 必须位于 `main` # [3.3.4-beta.2] - 2026-07-13 - **修复(#1078)**:同一番剧、媒体类型、季集、字幕组和分辨率出现更高修订版时,不再因目标文件已存在而反复重命名;默认 `hold` 保留双方并记录冲突,可选 `replace` 安全替换旧版本 - **新增(修订版替换)**:qBittorrent 与 aria2 使用可恢复的“暂存旧版 → 晋升新版 → 清理旧版”流程;异常重启、目标冲突和下载任务消失时可继续或回滚,首版只自动处理双方均为单文件的任务 - **新增(管理与通知)**:设置页增加全局修订冲突策略,新增冲突查询/重试 API 和通知中心事件 - **安全性**:多文件合集、身份信息不完整、非严格升级或同一路径存在多个下载器 owner 时一律保持 `hold`,不会自动删除旧任务或文件 - **测试**:覆盖 V1/V2 严格身份匹配、并发抢占、断点恢复、合集保护、qBittorrent/aria2 冲突与数据库迁移 # [3.3.4-beta.1] - 2026-07-10 - **新增(#1074,认证)**:引入多用户会话认证——首次安装创建管理员,支持用户管理、独立浏览器会话,以及按 `api` / `mcp` scope 隔离且仅展示一次明文的哈希令牌;密码变更、禁用用户和退出登录都会可靠撤销既有会话 - **修复(认证安全)**:刷新接口只续期 HttpOnly Cookie,不再向响应体或浏览器存储暴露令牌;一次性 API Token 在弹窗关闭、组件离开和延迟响应竞态下都会立即清除,移动端补齐 44px 可访问关闭按钮 - **修复(#1075)**:TMDB 电视与电影搜索严格使用解析设置中的语言;切换语言或修剪输入空白后,会以对应 locale 重新查询且不复用错误缓存 - **修复(#1072)**:`06v2` 等修正版集数与 CRC 后缀不再被误判为季/集偏移异常;真实超范围集数仍会进入 offset review - **修复(解析器)**:剧场版标题中的 `Movie` 等有效词不再被重复或误删,只移除末尾真正的发布类型标记 - **修复(#1065,发布)**:release trigger 分类改为完全由 event/ref/tag 决定,PR 标题不再影响版本;稳定 tag 仍必须位于 `main`,beta tag 可在 dev/测试分支安全发布 - **测试/CI**:新增完全隔离的运行时、Chromium、WebKit 与真实 qBittorrent E2E lanes,覆盖认证、重启持久化、Windows 路径、TMDB 语言、offset review、移动端规则编辑、播放器本地设置,以及 RSS 下载→校验→重命名→删除全链路 # [3.3.3] - 2026-07-09 - **新增**:AniBT 作为内置搜索源,默认使用 `https://anibt.net/rss/magnets.xml?q=%s` 并走 TMDB 解析;旧配置文件加载时会自动补齐新增的内置搜索源,同时保留用户已有搜索源的自定义 URL / 解析器 - **修复**(#1067):搜索结果预览会按“解析设置”中的 TMDB 语言重新取标题,不再在设置为日语等语言时回落到中文/英文标题;海报补全复用同一次 TMDB 预览请求并按语言缓存 - **修复**(#1068):删除/禁用番剧并选择 `file=true` 时,匹配 qBittorrent 种子的 `save_path` 会统一归一化路径分隔符,Windows qB 返回反斜杠路径时也能正确删除种子文件 - **修复**(#1069,WebUI):手机端编辑规则弹窗改为全屏底部面板,并关闭会把面板整体上推的软键盘补偿,避免作品名称输入框被悬浮栏/软键盘挤出可编辑区域 - **修复**(CI):稳定版发布只接受打在 `main` 上的语义化版本 tag,dev 分支仅允许 alpha/beta 预发布 tag,避免合并 PR 时误触发正式发布 # [3.3.2] - 2026-07-07 - **修复**:qBittorrent ≤5.1 对 .torrent 文件上传回笼统的 `200 "Fails."` 时,用文件字节计算 infohash 到 qB 确认是否已存在——已存在归类为重复并标记已下载,不再每轮 RSS 刷新无限重试报错("Fails." … "no matching torrent found" 循环,常见于清空孤儿记录后种子仍在 qB 的场景) - **修复**(WebUI):重复搜索时旧的搜索流未关闭——结果被重复追加(重复的条目/tag)、旧流无限自动重连导致输入框为空时仍持续转圈;现在开新搜索前先关旧流、追加结果按订阅链接去重、清空输入即停止搜索 # [3.3.1] - 2026-07-07 - **修复**:搜索/订阅/收集添加的种子未关联番剧被记成孤儿,导致孤儿记录无法通过 `track_orphans` 关闭;重复订阅时正确解析已存在的规则(不覆盖用户调好的 offset/filter),重新订阅已禁用的规则时自动重新启用;收集失败不再留下幽灵订阅规则 - **修复**(#721):标题中的 Windows/qB 保留字符(`< > : " / \ | ? *`)会把保存路径拆成多级目录或丢字——生成保存路径时清洗(仅新订阅,不触碰既有做种库) - **修复**(#667/#704):总集篇等半集(12.5)不再被取整成第 12 集覆盖原文件,重命名保留小数(`S01E12.5`),通知同步保留 - **新增**(#904):Server酱³ 推送支持——`sctp` 开头的 sendkey 自动走 `push.ft07.com` 端点 - **新增**(#147):重命名完成的种子在下载器中打 `ab:renamed` 标签,供外部脚本(filebot / hlink 等)过滤已处理任务 - **新增**(#975 / #1042):TMDB API Key 可自配(留空回退内置共享 key);设置页新增"网络设置"面板(TMDB / Bangumi API 地址与 Key) # [3.3.0] - 2026-07-07 3.3.0 正式版。主要变化(相对 3.2.x;逐项细节见下方 beta.1–beta.8 条目): - **全异步架构**:数据库层全面迁移到 `sqlite+aiosqlite`(WAL),仓储/服务/周期任务全部 async;组合根 `AppContext` 统一持有生命周期 - **下载器**:aria2 成为一等公民(能力协议 + 门面降级);qBittorrent 4.x–5.2 全版本兼容 - **WebUI**:Soft Ink 组件体系整体重构(22 个通用组件);种子管理页(孤儿/按番剧);PT 站(NexusPHP)搜索源预设;Passkey 登录与自动弹出 - **LLM**:提供商插件框架(内置/预设/订阅/可下载分组),签名验证的插件安装管线;插件(GitHub Copilot / ChatGPT Codex)随 `llm-plugins` release 发布 - **更新系统**:ed25519 验签的在线更新与回滚 - **安全**:破坏性 GET 端点转 POST(CSRF)、路径穿越修复、JWT/Passkey 加固 - 剧场版/特别篇支持、每集偏移建议、通知中心等 ### 自 beta.8 起新增 - **修复**(#1052):批量抓取种子文件加节流,HTTP 429 时指数退避 - **修复**(#1053):删除番剧时清理其派生的每番剧 RSS 订阅;搜索站订阅时正确映射站点对应的解析器 - **新增**:`track_orphans` 设置——关闭后不再记录未匹配种子(孤儿记录),后补规则可立即接住仍在源里的旧集 # [3.3.0-beta.8] - 2026-07-07 qBittorrent 5.x 全面兼容 + 种子管理页 + PT 站搜索源;日志不再"时有时无"。 ## Backend ### Fixed - **qBittorrent 5.x WebAPI 全面兼容**(对照 qB 各发布 tag 源码逐端点核验): - qB 5.2 的 `torrents/add` 返回 JSON 计数(200/202)而非 "Ok.",此前每次成功投递都被记为失败并无限重试(beta.7 用户日志中的 `rejected torrent add: HTTP 200 …success_count:1` 与循环 409);现正确识别 200/202/JSON/409,部分成功与 pending(URL 异步抓取)均按成功处理,409 对文件上传视为重复、磁力链经 hash 确认,无法确认时抛出留待重试 - qB 5.0 把 `torrents/pause|resume` 改名为 `stop|start` 且无别名:此前对全部 5.x 暂停/恢复是静默空操作;现先试新名、404 时回退旧名并记住 - `torrents/info?filter=paused` 在 5.x 被静默当作 All(MCP `list_downloads` 会返回全部种子):paused/stopped 改为本地按 state 过滤 - qB 5.2 空响应体统一回 204:`torrents/delete` 误报失败、`torrents/renameFile` 误判失败导致重命名每周期重试,均改为按 2xx 判定 - `torrents/add` 同时发送 `paused` 与 `stopped` 参数(5.0 改名,双方各自忽略未知参数) - **日志时有时无**:启动(容器重启/在线更新)时日志被整个删除,改为轮转进 `log.txt.1-3` 备份;轮转后 UI 只读 `log.txt` 导致历史瞬间清空,现预算内自动拼接备份尾部;轮转竞态不再可能炸掉 SSE 流;清空日志同时删除备份;总量上限约 8 MB - **孤儿种子持续累积无处清理**(#818 / #885 / #1010,#1020 的 3.3 移植):新增孤儿种子与按番剧种子的查询/删除端点 ### Changed - **Docker 镜像瘦身 208 MB → 160 MB(拉取体积 −24%)**:不再预编译字节码(root 属主 venv 下本就无法持久化);去掉未使用的 `mcp[cli]` 附加依赖(typer/rich 等约 14 MB) ## Frontend ### Added - **种子管理页**:番剧网格新增"未匹配种子"卡片(计数徽标),孤儿种子与按番剧种子列表支持多选、批量删除、一键清空(Promise 确认框);规则编辑弹窗新增"查看种子"入口 - **PT 站(NexusPHP)搜索源预设**:添加搜索源对话框新增 PT 站模式,填站点地址 + Passkey +(可选)分类 ID 即可生成 `torrentrss.php` 搜索模板(分类用 `cat=1` 逐分类开关形式,纯数字校验,预览遮蔽 Passkey);注意原生新版 NexusPHP 已停用 search 参数,请先在站点上确认 RSS 搜索可用 - **Passkey 自动弹出**:本浏览器成功用过 Passkey 后,打开登录页自动弹出认证;取消则本浏览器解除自动弹出(下次 Passkey 登录成功后重新记住),主动登出后的一次跳转不弹,避免反射式指纹确认又把用户登回去 ### Fixed - **登录页密码框按回车不登录**:登录按钮现在是表单的 submit 按钮(此前 type=button,双输入框表单无提交按钮时浏览器不做隐式提交) - 下载页种子状态适配 qB 5.0+ 的 `stoppedDL/stoppedUP` 命名(此前显示原始状态串) # [3.3.0-beta.7] - 2026-07-06 发版前 ship-readiness 审查(多代理评审 + 对抗验证)确认的 10 个缺陷全部修复;本版为 3.3.0 稳定版前的收尾修复版。 ## Backend ### Fixed - **保存设置可能静默写坏通知渠道密钥**:掩码密钥(`********`)按下标回填,删除/重排列表项后幸存项会错拿被删项的密钥且原值永久丢失。现按身份匹配回填(同下标同身份 → 全列表唯一身份 → 长度不变时按下标兜底),无法唯一定位来源时返回 400 提示重新输入密钥,绝不猜 - **生产构建下自托管 Inter 字体 404**:`/fonts` 未挂载静态目录,字体请求被 SPA 兜底路由回成 index.html,整站字体静默回退系统字体(开发模式由 Vite 直接服务 `public/`,故此前未暴露) - **受限 UMASK(如 077)下在线更新后崩溃循环**:boot_overlay 以 root 解包/复制的文件是 600/700 root:root,以 ab 运行的应用读不了 `/app/module`。现每棵落地的树(module / dist / .venv,含 EXDEV 恢复路径)都交还应用用户,可读性与 UMASK 无关 - **EXDEV 就地替换非原子**:中途失败(磁盘满/IO 错误)会留下"删光了但没灌满"的残缺 module 树,启动进入必然 ImportError 崩溃循环。现先快照旧树到 `.ab_bak`,失败时恢复旧树再放弃本次覆盖 - **缺留存备份 bundle 时回滚会把 bundle.zip 换丢**:boot_overlay 下次启动判定 legacy/unsigned 整体清除覆盖层,实际落在镜像版本却报告"已回滚到上一版本"。现该场景改走"回退镜像版本"路径并如实报告,同时清掉无法验签的孤儿 backup 树 - **qBittorrent 凭据错误仍会累积触发 WebUI IP ban**:"不重试"只在单次 auth 内生效,每个周期 tick 仍各发一次失败登录,约 5 次即被 qB 封禁 IP。现凭据被拒后进程级闩锁(保存设置后解锁重试);auth 单飞化,并发等待者共享失败结论、不再补发 POST - **下载器设置变更的生命周期竞态**:正在登录(尚未计入引用计数)的客户端不再被并发的设置变更块中途登出;连续两次改设置不再把第一个被撤下客户端顶掉导致连接池泄漏;`__aenter__` 中途被取消不再泄漏引用计数 - **entrypoint 属主迁移 marker 先写后 chown**:大海报缓存迁移中途被打断(停容器/崩溃/NAS 报错)会被记成"已完成"、之后每次启动跳过且永不自愈。现 `chown -R` 成功退出后才写 marker - **aria2 下载列表在 WebUI 显示 undefined / NaNhNaNm**:`torrents_info` 载荷缺少下载页无条件消费的字段。现补齐 `num_seeds`/`num_leechs`(numSeeders/connections)、`eta`(qB 的 8640000=未知约定)、`added_on`(gid 映射入库时间,用于排序),并把 aria2 状态映射为 qB 状态词汇(active→downloading/uploading 等) ### Changed - 程序控制端点(`/restart` `/start` `/stop` `/shutdown`)恢复 GET 别名(标记 deprecated,计划下个大版本移除):3.2 及更早版本这些端点是 GET,外部自动化(cron / Home Assistant 等)升级后不应 405 静默失效 # [3.3.0-beta.6] - 2026-07-06 ## Frontend ### Changed - **Soft Ink 组件体系重构**:WebUI 的通用组件层整体重设计为"Soft Ink"语言——近单色的墨色文字 + 柔和填充控件,语义色只以小方块标记出现、不再承载文字(去掉粉彩胶囊标签、着色警告框、悬浮滑块分段器等模板化样式) - 新增 22 个通用组件(`ab-button`/`ab-icon-button`/`ab-menu`/`ab-split-button`/`ab-modal`/`useConfirm()`/`ab-field`/`ab-input`/`ab-select`/`ab-switch`/`ab-segmented`/`ab-tag` v2/`ab-status` v2/`ab-badge`/`ab-progress`/`ab-alert`/`ab-empty`/`ab-skeleton`/`ab-list`/`ab-toolbar`/`ab-tooltip` 等),全部带 vitest 测试与 Storybook story;页面全量迁移,不再直接使用裸 `NButton`/`