架构说明
res-downloader 是一个基于 Wails 的跨平台桌面应用。Vue 前端负责交互与状态展示,Go 后端负责本地代理、证书与系统集成、资源识别、插件运行和下载任务。应用通过版本化插件协议扩展站点能力,避免将频繁变化的网站逻辑直接写入宿主。
本文介绍模块边界、运行时依赖和主要数据流。插件字段、权限和运行时 API 的完整定义请查看插件开发指南。
总体架构
浏览器 / 手机 / 桌面应用
│ HTTP / HTTPS 代理流量
▼
┌──────────────────────────────────────────────────────────────┐
│ server.Gateway:统一监听 Host:Port │
│ │
│ 本地 /api 请求 ──► httpapi.Server │
│ 其他请求 ──► proxy.Engine(goproxy) │
└──────────┬───────────────────────────────┬───────────────────┘
│ │
│ 配置、资源、任务、插件管理 │ 请求/响应观察结果
▼ ▼
┌──────────────────┐ ┌─────────────────────────────┐
│ Runtime 业务服务 │ │ plugin.PluginManager │
│ │ │ - 内置通用识别器 │
│ resource.Resource│◄────────────│ - 应用内嵌官方插件 │
│ download.Scheduler │ - 用户安装插件 │
│ media.Engine │ └──────────────┬──────────────┘
│ system.Setup │ │ ResourceCandidate
└─────────┬────────┘ ▼
│ 资源目录、关联与持久化
│ HTTP 响应 / Wails 事件 │
▼ │
┌───────────────────────────────────────────────┴──────────────┐
│ Vue + Pinia 前端:资源列表、任务中心、插件管理、系统设置 │
└──────────────────────────────────────────────────────────────┘internal/app.Runtime 是应用的组合根。它创建各模块并注入依赖,但构造阶段不会启动监听器或后台任务。
CLI / MCP 通过独立的本机控制入口调用现有 HTTP 业务处理器:Agent / Shell → internal/automation → internal/control → httpapi.ControlHandler → 资源与下载服务。客户端不构造桌面 Runtime、不打开业务数据库,也不启动下载调度器。
运行时组成
| 模块 | 主要职责 | 关键依赖或输出 |
|---|---|---|
| Wails | 加载内嵌前端、提供窗口生命周期和少量 Bind 方法 | frontend/dist、app.Bind |
app.Runtime | 创建模块、连接依赖、管理启动与关闭顺序 | 配置、事件、代理、HTTP、插件、资源、下载 |
server.Gateway | 管理抓取代理监听器,并在本地 API 与代理请求之间分流 | httpapi.Server、proxy.Engine |
control.Server | 管理本机自动化监听器、独立凭据及连接发现文件 | 仅监听回环地址,复用资源与下载 HTTP 处理器 |
automation | 将 CLI 命令与 MCP stdio 工具转换为本机控制请求 | 每次调用读取当前连接信息,不启动桌面 Runtime |
proxy.Engine | 处理 HTTP 代理和 HTTPS MITM,生成请求/响应观察结果 | 拦截规则、设备证书、插件管理器 |
plugin.PluginManager | 加载插件、执行观察钩子、关联资源、刷新地址和生成下载计划 | 内置、内嵌和用户插件 |
resource.Resource | 维护资源目录、持久化候选资源、执行资源操作和下载计划 | resources.db、捕获缓存、媒体引擎 |
download.Scheduler | 持久化任务、管理工作协程和任务状态,处理暂停、恢复与重试 | tasks.db、插件下载计划 |
download.PlanRunner | 执行一个下载计划中的输入获取、处理和输出安装 | HTTP、HLS、捕获文件、FFmpeg、WASM |
capture.Store | 暂存代理或页面脚本捕获的响应字节,供下载计划读取 | capture-cache/ |
media.Engine | 调用用户配置的 FFmpeg / ffprobe 完成媒体处理 | 转封装、合并、录制等能力 |
system.Setup | 管理设备证书、系统代理和平台相关操作 | Windows、macOS、Linux 实现 |
events.Emitter | 把资源和任务变化编码后发送给 Wails 前端 | 单一 event 事件通道 |
启动和关闭生命周期
构造阶段
NewRuntime 按依赖顺序完成以下工作:
- 创建应用目录、日志和事件发送器。
- 初始化当前设备的证书颁发机构;证书失败会记录在应用状态中,由界面提示用户处理。
- 加载配置并创建响应捕获缓存、媒体引擎和系统集成服务。
- 打开资源数据库并恢复资源目录。
- 加载拦截规则、插件状态、内嵌插件和用户插件。
- 打开任务数据库并恢复下载队列。
- 将插件、资源服务和下载调度器互相连接。
- 创建代理引擎、本地 API 和统一 HTTP 网关。
配置应用后,运行时会按需更新上游代理传输、下载工作协程数量和 HTTPS 拦截规则,不需要重新创建整个应用。
启动阶段
Runtime.Start 依次初始化代理处理器、启动统一 HTTP 网关,再启动下载调度器。下载调度器会恢复持久化任务:仍处于等待状态的任务会重新排队,先前正在解析、下载或处理的任务则标记为已中断,等待用户决定是否恢复或重试。
随后启动 control.Server,监听 127.0.0.1 的动态端口,写入受当前用户访问权限保护的 control/session.json。该入口使用独立随机 Token 和路由白名单,不受抓取代理 Host / Port 设置影响。自动化服务启动失败时记录日志,桌面抓取和下载继续运行。
关闭阶段
Runtime.Close 首先关闭自动化入口并移除属于当前实例的连接文件,再尝试关闭应用管理的系统代理,然后停止 HTTP 网关和下载调度器,最后关闭资源数据库、捕获缓存和日志。清理并重置应用时,也会在这些资源释放后删除应用状态并重新启动程序。
资源捕获流程
- 用户开启系统代理,或手动让其他设备和应用连接到配置的监听地址。
server.Gateway接收连接。已识别的本地/api请求交给 HTTP API,其他请求交给proxy.Engine。- HTTPS CONNECT 请求先由
rules.Set根据域名策略决定进行 MITM 还是直接透传。该阶段只有主机信息,资源类型和 MIME 匹配在后续观察阶段处理。 - 代理把请求或响应转换成版本化的
Observation。只有匹配规则和权限需要时,才读取受bodyLimit限制的 Body。 plugin.PluginManager按优先级调用已启用插件,包括内置通用识别器、应用内嵌官方插件和用户安装插件;用户触发page-command资源操作时,它还会把宿主生成的标准消息投递给同一插件声明的桥接页面脚本。- 插件可以输出资源候选、关联多次请求、请求受控的响应修改或页面脚本,并在声明捕获任务时把响应字节写入
capture.Store。 - 插件输出的
ResourceCandidate由资源服务规范化、关联并保存到内存目录和resources.db。 - 资源变化通过 Wails 事件推送到前端,前端再按筛选条件更新资源列表。
代理对请求 Body 的读取会把已读取前缀重新接回原始流;响应观察也会保留完整响应供客户端继续消费。插件观察不应改变普通代理传输,除非插件显式申请并返回了受校验的修改结果。
下载执行流程
- 前端调用本地 API 创建下载任务,
download.Scheduler生成任务记录并写入tasks.db。 - 调度器根据配置的并发数从队列取出任务。集合资源会拆分为多个子项,但仍保留一个父任务状态。
- 如果资源已过期或标记为需要刷新,调度器先请求原插件刷新资源;无法刷新时返回需要重新捕获的明确状态。
- 插件为资源生成
DownloadPlan。计划描述一个或多个输入、所需执行器、处理步骤和最终输出,而不是直接控制宿主对象。 resource.Resource根据文件名模板和重名策略计算最终路径,再把计划交给download.PlanRunner。- PlanRunner 根据计划使用普通 HTTP、HLS、捕获文件或 FFmpeg 等执行器获取输入,并按需执行宿主处理或插件 WASM 处理器。
- 临时结果完成后原子化安装到最终路径;进度和状态同时写入任务数据库并通过事件发送给前端。
普通 HTTP 文件和捕获文件计划可以保留断点状态;直播录制、HLS 或媒体处理是否支持暂停恢复由具体执行器决定。应用退出时未完成的任务会记录为可恢复或已中断状态,而不是继续持有后台进程。
前后端通信
前端不会直接引用 Go 内部模块,主要通过三条通道通信:
- Wails Bind:提供应用信息、配置快照、API Session Token 和重置入口等少量启动能力。
- 本地 HTTP API:处理资源、预览、下载任务、插件管理、证书和设置请求。除少数公开的证书下载或 HLS 预览路径外,请求需要本次启动随机生成的 Bearer Token,并校验来源和请求方法。
- Wails 事件:后端将不同业务事件封装为
{type, data},统一通过event通道发送;Pinia 事件 Store 再分发给各页面。
Wails AssetServer 使用同一套 HTTP API 中间件,使内嵌前端可以访问 /api。独立监听器上的网关则让代理、手机证书下载和本地预览复用配置的 Host 与 Port。
CLI 与 MCP 另经本机控制监听器访问资源查询及下载任务操作。控制入口拒绝浏览器 Origin、非本机 Host 和白名单之外的路由;控制 Token 不能直接用于桌面 API。客户端不使用环境代理、不跟随重定向,也不自动重试写操作。MCP 使用官方 Go SDK 处理 stdio 协议,标准输出仅写协议消息;业务进度通过工具查询获取。使用方法见 CLI 与 MCP。
插件系统边界
插件接收结构化观察结果并返回结构化候选资源或下载计划,不直接持有 Go 对象,也不能任意访问文件系统、Shell 或宿主任意网络接口。宿主在加载和执行阶段校验 Manifest、域名、capability、Body 上限、页面脚本、资源操作、下载计划和处理器声明。page-command 只把已保存资源中的插件自定义参数发送给同插件指定的桥接页面脚本,不开放通用页面控制接口给桌面前端。
插件来源分为三类:
- 内置插件:由 Go 实现,例如通用资源识别器。
- 内嵌官方插件:源码快照位于
internal/plugin/bundled/,随应用发布并安装到用户插件目录。 - 用户插件:安装在用户数据目录的
plugins/下,可以来自扩展商店或本地 ZIP。
站点专用的接口识别、数据关联、签名计算、地址刷新和非 DRM 字节转换应保留在插件中。只有多个插件都可能复用、且当前协议无法安全表达的能力,才应扩展宿主 API;扩展时需要同步 Go 模型、运行时校验、Schema、TypeScript 声明、示例和文档。
更多细节请查看插件开发指南、插件 SDK v1和扩展商店发布说明。
数据与状态
应用状态位于操作系统为 res-downloader 分配的用户配置目录。主要内容如下:
| 路径 | 内容 | 生命周期 |
|---|---|---|
config.json | 界面、监听地址、代理、下载、命名和媒体工具设置 | 设置保存时原子更新 |
logs/app.log 及带时间戳的备份 | 正式版应用日志和已开启的插件调试日志 | 单文件 10 MiB,最多 5 个备份;打开和轮转时清理超过 7 天的备份,详见日志说明 |
mitm-ca.crt、mitm-ca.key | 当前设备生成的 HTTPS 拦截证书和私钥 | 重置应用时清理 |
resources.db | 已发现资源的持久化目录 | 清空资源或重置时更新 |
tasks.db | 下载任务、子项、进度和恢复状态 | 任务变化时更新 |
control/session.json | 本次启动的自动化地址和独立 Token | 自动化服务启动时写入,正常关闭时删除,异常退出后由下次启动替换 |
capture-cache/ | 代理响应和页面媒体片段的临时字节缓存 | 有有效期,并在重置时清理 |
plugins/ | 当前安装的外部和内嵌插件副本 | 安装、升级、回滚或卸载时更新 |
plugin-state.json | 插件启用状态 | 插件设置变化时更新 |
plugin-settings.json | 各插件的用户配置 | 保存插件设置时更新 |
plugin-removed.json | 用户主动移除的内嵌插件记录 | 防止升级时意外恢复 |
plugin-sources.json | 已安装插件的来源信息 | 安装和升级时更新 |
plugin-backups/ | 外部插件升级时保留的一次回滚副本 | 升级、回滚和卸载时更新 |
资源库或任务库无法打开时,相应模块会记录错误并退化为内存状态;这能让应用继续启动,但本次状态无法跨重启保存。
主要目录
| 目录 | 职责 |
|---|---|
frontend/ | Vue、Pinia、Naive UI 前端及 Wails 生成的桥接代码 |
internal/app/ | 应用组合根、Wails Bind 和各后端模块的适配层 |
internal/server/ | 统一监听器及 API / 代理分流,不包含业务处理 |
internal/control/ | 本机控制监听器、连接发现及跨平台凭据文件权限 |
internal/automation/ | CLI 参数、MCP 工具和共用的本机 API 客户端 |
internal/httpapi/ | 本地 API、鉴权、预览和桌面操作入口 |
internal/proxy/ | HTTP 代理、HTTPS MITM、观察结果和页面脚本注入 |
internal/rules/ | CONNECT 阶段的域名拦截与透传策略 |
internal/plugin/ | 插件加载、权限校验、运行时、商店、安装和开发 CLI |
internal/resource/ | 资源目录、关联、持久化、资源操作和下载计划入口 |
internal/download/ | 任务调度、状态持久化、下载执行器和计划运行器 |
internal/capture/ | 可按范围写入或追加片段的临时响应缓存 |
internal/media/ | FFmpeg / ffprobe 能力检测与进程执行 |
internal/system/ | 证书、系统代理和平台差异实现 |
internal/config/ | 默认配置、校验、快照和持久化 |
internal/events/ | Go 到 Wails 前端的事件发送 |
internal/model/ | 跨模块共享的资源、插件和下载协议模型 |
internal/naming/ | 下载文件名模板和重名策略 |
internal/logging/ | 应用日志封装 |
examples/plugins/ | 可提交的插件协议示例和脱敏 fixture |
plugins/ | 独立站点插件的本地开发工作区,默认不随宿主提交或打包 |
cmd/extension-index/ | 根据 GitHub 仓库生成扩展商店索引的脚本 |
cmd/resdctl/ | 可独立构建的 CLI / MCP 控制台客户端,仍依赖运行中的桌面应用 |
build/ | Wails 平台配置、图标和安装包构建资源 |
docs/ | 用户指南、插件 SDK 和开发文档 |
修改边界
- 新增或修复站点适配时,优先修改独立插件,不要把网站私有逻辑加入
internal/proxy/或internal/resource/。 - 扩展插件宿主能力时,先定义通用协议和权限边界,再同步运行时、校验、SDK 文件、示例和文档。
- 修改本地 API 时,同时检查来源、鉴权、方法、Body 上限和前端类型。
- 修改下载状态或资源模型时,同时检查 BoltDB 恢复逻辑、事件负载和前端状态映射。
- 修改启动或关闭流程时,保持构造函数无监听器和后台任务副作用,并确保失败路径释放已经创建的资源。
- 平台相关的证书和代理行为应留在
internal/system/,公共业务层通过接口或适配器调用。
