跳转到正文

架构说明 ​

res-downloader 是一个基于 Wails 的跨平台桌面应用。Vue 前端负责交互与状态展示,Go 后端负责本地代理、证书与系统集成、资源识别、插件运行和下载任务。应用通过版本化插件协议扩展站点能力,避免将频繁变化的网站逻辑直接写入宿主。

本文介绍模块边界、运行时依赖和主要数据流。插件字段、权限和运行时 API 的完整定义请查看插件开发指南。

总体架构 ​

text
浏览器 / 手机 / 桌面应用
          │ 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 是应用的组合根。构造阶段只准备应用信息、配置、日志和事件对象;业务资源在 Wails 单实例检查之后初始化。

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加载插件、执行观察钩子、关联资源、刷新地址和生成下载计划内置、内嵌和用户插件
operation.Service操作发现、会话选择、单次/批量、调度、取消、历史、结果和产物独立 operations.db、插件页面桥、资源与下载适配
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 准备应用目录、日志、配置和事件对象,供窗口尺寸、Bind 和中间件配置使用,不打开业务数据库或加载插件。

启动阶段 ​

桌面入口使用 Wails SingleInstanceLock;重复启动会唤起已有窗口。OnDomReady 异步调用 Runtime.Start,在单实例检查之后依次初始化设备证书、捕获缓存、资源数据库、插件、任务数据库、代理和 HTTP 服务。前端静态资源可立即加载,应用信息和 API 请求等待后端就绪;初始化失败会释放已创建的业务资源。退出时取消后续启动阶段,当前同步初始化操作完成后再安全清理。

配置应用后,运行时会按需更新上游代理传输、下载工作协程数量和 HTTPS 拦截规则,不需要重新创建整个应用。

Runtime.Start 依次初始化代理处理器、启动统一 HTTP 网关,再启动下载调度器。下载调度器会恢复持久化任务:仍处于等待状态的任务会重新排队,先前正在解析、下载或处理的任务则标记为已中断,等待用户决定是否恢复或重试。

随后启动 control.Server,监听 127.0.0.1 的动态端口,写入受当前用户访问权限保护的 control/session.json。该入口使用独立随机 Token 和路由白名单,不受抓取代理 Host / Port 设置影响。自动化服务启动失败时记录日志,桌面抓取和下载继续运行。

关闭阶段 ​

App 统一管理退出、重启、重置和更新交接;Runtime.Close 只释放业务资源:关闭自动化入口、移除当前实例的连接文件、关闭应用管理的系统代理、停止 HTTP 网关和下载调度器,再关闭资源数据库与捕获缓存。清理成功后,App 按需执行后续动作,最后关闭日志;清理失败则取消更新,不重置或启动新进程。资源清理和后续动作均避免重复执行。

改端口或重置后的新进程会等待旧进程退出,再进入单实例检查。更新助手和 CLI / MCP 不进入桌面单实例流程。

资源捕获流程 ​

  1. 用户开启系统代理,或手动让其他设备和应用连接到配置的监听地址。
  2. server.Gateway 接收连接。已识别的本地 /api 请求交给 HTTP API,其他请求交给 proxy.Engine。
  3. HTTPS CONNECT 请求先由 rules.Set 根据域名策略决定进行 MITM 还是直接透传。该阶段只有主机信息,资源类型和 MIME 匹配在后续观察阶段处理。
  4. 代理把请求或响应转换成版本化的 Observation。只有匹配规则和权限需要时,才读取受 bodyLimit 限制的 Body。
  5. plugin.PluginManager 按优先级调用已启用插件,包括内置通用识别器、应用内嵌官方插件和用户安装插件;声明的业务操作由统一操作服务投递给明确选定的桥接页面。
  6. 插件可以输出资源候选、关联多次请求、请求受控的响应修改或页面脚本,并通过响应捕获或 api.capture.save 把文件字节写入 capture.Store。
  7. 插件输出的 ResourceCandidate 由资源服务规范化、关联并保存到内存目录和 resources.db。
  8. 资源变化通过 Wails 事件推送到前端,前端再按筛选条件更新资源列表。

代理对请求 Body 的读取会把已读取前缀重新接回原始流;响应观察也会保留完整响应供客户端继续消费。插件观察不应改变普通代理传输,除非插件显式申请并返回了受校验的修改结果。

下载执行流程 ​

  1. 前端调用本地 API 创建下载任务,download.Scheduler 生成任务记录并写入 tasks.db。
  2. 调度器根据配置的并发数从队列取出任务。集合资源会拆分为多个子项,但仍保留一个父任务状态。
  3. 如果资源已过期或标记为需要刷新,调度器先请求原插件刷新资源;无法刷新时返回需要重新捕获的明确状态。
  4. 插件为资源生成 DownloadPlan。计划描述一个或多个输入、所需执行器、处理步骤和最终输出,而不是直接控制宿主对象。
  5. resource.Resource 根据文件名模板和重名策略计算最终路径,再把计划交给 download.PlanRunner。
  6. PlanRunner 根据计划使用普通 HTTP、HLS、捕获文件或 FFmpeg 等执行器获取输入,并按需执行宿主处理或插件 WASM 处理器。
  7. 临时结果完成后原子化安装到最终路径;进度和状态同时写入任务数据库并通过事件发送给前端。

调度器持有每次任务执行的取消上下文,并通过父子上下文传递给 PlanRunner 和输入执行器。资源服务只执行计划、处理命名与输出安装,不持有调度器或维护下载实例索引。执行进度和处理阶段通过绑定任务 ID 与执行上下文的回调上报,已取消执行的迟到回调会被忽略。集合任务按完成子项数统计进度,子项字节进度不会更新同一资源的独立下载任务。资源列表和任务中心共享 downloadTaskUpdated 事件。

普通 HTTP 文件和捕获文件计划可以保留断点状态;直播录制、HLS 或媒体处理是否支持暂停恢复由具体执行器决定。应用退出时未完成的任务会记录为可恢复或已中断状态,而不是继续持有后台进程。

前后端通信 ​

前端不会直接引用 Go 内部模块,主要通过三条通道通信:

  • Wails Bind:提供应用信息、配置快照、API Session Token 和重置入口等少量启动能力。
  • 本地 HTTP API:处理资源、预览、下载任务、插件管理、证书和设置请求。除少数公开的证书下载或 HLS 预览路径外,请求需要本次启动随机生成的 Bearer Token,并校验来源和请求方法。
  • Wails 事件:后端将不同业务事件封装为 {type, data},统一通过 event 通道发送;Pinia 事件 Store 再分发给业务 Store 和页面。

下载任务由共享的 Pinia Store 统一加载,并处理 downloadTaskUpdated 和 downloadTaskRemoved 事件。任务中心按任务 ID 展示,资源列表按资源 ID 查询最新关联任务,不在资源行维护另一份可写下载状态。列表加载期间收到的更新和删除会保留,较旧的任务快照不会覆盖较新的状态。

Wails AssetServer 使用同一套 HTTP API 中间件,使内嵌前端可以访问 /api。独立监听器上的网关则让代理、手机证书下载和本地预览复用配置的 Host 与 Port。

CLI 与 MCP 另经本机控制监听器访问资源查询及下载任务操作。控制入口拒绝浏览器 Origin、非本机 Host 和白名单之外的路由;控制 Token 不能直接用于桌面 API。客户端不使用环境代理、不跟随重定向,也不自动重试写操作。MCP 使用官方 Go SDK 处理 stdio 协议,标准输出仅写协议消息;业务进度通过工具查询获取。使用方法见 CLI 与 MCP。

插件系统边界 ​

插件接收结构化观察结果并返回结构化候选资源或下载计划,不直接持有 Go 对象,也不能任意访问文件系统、Shell 或宿主任意网络接口。宿主在加载和执行阶段校验 Manifest、域名、capability、Body 上限、页面脚本、资源操作、下载计划和处理器声明。业务操作必须声明输入输出 Schema、页面脚本和 effects;资源按钮的输入由宿主从已保存资源中取得并校验归属。网站私有接口保留在插件内。

插件来源分为三类:

  • 内置插件:由 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已发现资源的持久化目录清空资源或重置时更新
operations.db业务操作摘要、允许保存的结果、关联、设置和幂等记录默认摘要 7 天、结果 2 小时,支持调整保留时间和清理;重置应用时删除
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/operation/统一业务操作服务及独立 BoltDB 存储
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/githubctl/生成正式版更新信息和扩展商店索引的脚本
cmd/resdctl/可独立构建的 CLI / MCP 控制台客户端,仍依赖运行中的桌面应用
build/Wails 平台配置、图标和安装包构建资源
docs/用户指南、插件 SDK 和开发文档

修改边界 ​

  • 新增或修复站点适配时,优先修改独立插件,不要把网站私有逻辑加入 internal/proxy/ 或 internal/resource/。
  • 扩展插件宿主能力时,先定义通用协议和权限边界,再同步运行时、校验、SDK 文件、示例和文档。
  • 修改本地 API 时,同时检查来源、鉴权、方法、Body 上限和前端类型。
  • 修改下载状态或资源模型时,同时检查 BoltDB 恢复逻辑、事件负载和前端状态映射。
  • 修改启动或关闭流程时,保持构造函数无监听器和后台任务副作用,并确保失败路径释放已经创建的资源。
  • 平台相关的证书与抓取代理设置应留在 internal/system/,更新请求的代理解析应留在 internal/netproxy/,公共业务层通过接口或适配器调用。

参与代码开发前请阅读参与贡献。涉及插件时,还应阅读插件开发指南及对应 SDK 文档。

业务操作生命周期 ​

Runtime 在资源、插件和下载服务就绪后创建操作服务,再提供桌面与控制 API。operation.Service 通过后端接口发现插件/页面、发送调用并整理资源与下载结果,不包含网站逻辑。页面 handler 执行异步业务;Goja 的同步钩子仅整理结果。启动将未完成记录标记 interrupted,不重放页面行为;关闭操作服务后再释放它依赖的插件、任务和资源。存储不可用时明确拒绝操作。

页面桥上报携带执行领取时的 revision,不能用当前页面 revision 替代。页面状态变化后,旧执行仅可确认 handler 已结束并释放占用,不能提交结果或覆盖终态。普通页面消息与操作就绪上报共享页面上下文,通过页面会话锁读写。

资源行通过桌面专用 /api/operations/resources 按资源 ID 查询状态,每次最多 1000 个;同一资源优先返回活动执行,再按创建时间选最新记录,不返回结果正文。该查询只覆盖当前筛选后的资源及其子项,独立于历史分页;前端离开资源页或窗口隐藏时停止轮询。历史列表仅深拷贝当前分页的摘要,避免复制全部结果。

操作页按当前内容刷新:历史页刷新历史,运行页刷新页面会话,打开的执行详情和活动批次刷新状态;操作定义与服务设置降低刷新频率。窗口隐藏时不发送轮询请求。

保留清理复用最近一次扫描,直到记录写入、设置变化、手动清理或最近的到期时间;最长间隔一分钟。结果和产物仍在到期后立即失去读取权限。重置应用时一并删除 operations.db,避免留下引用已清空资源和任务的历史、设置或幂等记录。详细状态、限额和保留规则见插件业务操作。

请确保对所处理的资源拥有合法权利,并遵守所在地法律、平台协议和版权规定。