- 音视频
- 桌面应用
- 后端
【免费下载链接】mediago
跨平台视频提取工具:支持流媒体下载、视频下载、m3u8 下载及 B站视频下载,提供 Windows 和 Mac 桌面客户端。Cross-platform video extraction tool: Supports streaming download, video download, m3u8 download, and Bilibili video download, with desktop clients for Windows and Mac.
导读
本文围绕 MediaGo 开源仓库中的packages/electron-preload包,深入剖析桌面端 Electron 主进程与渲染进程之间的安全桥接层:它通过contextBridge将经过类型约束的平台能力(浏览器标签、对话框、Shell、上下文菜单、CLI、更新等)以window.electron形式暴露给前端,同时借助@mediago/common中统一的 IPC 通道常量与PlatformApi接口保证类型安全与可维护性。读完本文,你将理解 preload 脚本的职责边界、完整 API 面、主进程集成方式,以及它在 MediaGo「Go Core HTTP 负责业务、Electron IPC 负责平台能力」这一分层架构中的具体位置。
包定位:MediaGo 三层架构中的「桥」
MediaGo 桌面端是一个典型的 Electron + Go Core 混合架构应用。从仓库结构看,apps/electron(Electron 主进程)、packages/electron-preload(渲染进程桥接层)与packages/common(跨端共享类型与 IPC 通道常量)三者协同工作:
- 业务数据(GoApi):下载任务、转换、收藏等 CRUD 操作由渲染进程直接请求 Go Core 的 HTTP 服务,不经过 Electron IPC。
- 平台能力(PlatformApi):只有依赖桌面宿主能力的操作(创建浏览器标签、打开文件对话框、读取系统语言、触发应用更新等)才通过 IPC 进入主进程。
packages/electron-preload正是后者的实现载体。它的 README 开门见山:该包通过 Electron 的contextBridge向渲染进程暴露安全 API,核心特征包括安全 IPC 通信、类型安全的 API 定义、集中化的 preload 逻辑以及基于 tsdown/Rolldown 的优化构建。
安全模型:为什么需要 preload 桥
Electron 的安全实践要求渲染进程关闭 Node 集成、开启上下文隔离。preload 脚本是唯一能在页面加载前于渲染进程中执行、又拥有完整 Node/Electron 能力的桥梁。MediaGo 的主进程配置与之严格对应(见apps/electron/src/utils/index.ts中的preloadUrl解析,以及apps/electron/src/services/browser-tab-manager.service.ts中对require.resolve("@mediago/electron-preload")的使用):
import { join } from "path"; import { BrowserWindow } from "electron"; const win = new BrowserWindow({ webPreferences: { nodeIntegration: false, contextIsolation: true, preload: join( __dirname, "../node_modules/@mediago/electron-preload/build/preload.js", ), }, });参数说明:
nodeIntegration: false:渲染进程无 Node 权限,页面内的任意第三方脚本都无法直接触碰文件系统或进程;contextIsolation: true:渲染进程与 preload 各自拥有独立的 JavaScript 上下文,隔离全局对象;preload:指向本包构建产物(@mediago/electron-preload包的主入口为build/index.cjs,见 package.json)。
在这种模型下,渲染进程唯一可用的「特权入口」就是 preload 显式暴露的 API——攻击面从「整个 Electron 能力全集」收敛为「开发者精心挑选的一小组方法」。
API 面:window.electron上暴露了什么
preload 脚本的核心逻辑位于 src/index.ts。它从@mediago/common导入IPC常量与全部相关类型,构造出electronApi对象后调用:
contextBridge.exposeInMainWorld("electron", electronApi);于是渲染进程中即可通过window.electron访问以下能力(全部基于ipcRenderer.invoke的 Promise 风格双向通信):
| 命名空间 | 方法 | 说明 |
|---|---|---|
browser | createTab/activateTab/closeTab/getTabs/loadURL/back/reload/show/hide/home/setBounds/setDeviceMode/clearCache/pluginReady/showDownloadDialog/dismissOverlayDialog | 桌面内置浏览器的标签页生命周期、导航、窗口尺寸、设备模拟、缓存清理、插件就绪、下载对话框与浮层控制 |
app | getEnvPath/getPathForFile/getExtensionDir/getPreferredSystemLanguage/getSharedState/setSharedState/showBrowserWindow/combineToHomePage/drainShareIntents | 环境路径、浏览器扩展目录、系统语言、共享状态、窗口唤起与分享意图消费 |
dialog | open/save | 系统级打开/保存对话框 |
shell | open | 用系统默认程序打开目标(如扩展目录) |
contextMenu | show | 显示自定义上下文菜单 |
cli | getStatus/install | 内置 CLI 工具的状态查询与安装 |
update | getState/check/startDownload/install/openLogDirectory/getDiagnosticInfo | 应用更新全生命周期控制 |
| 事件 | on/off | 订阅/退订主进程推送的事件(底层为ipcRenderer.on/removeListener) |
两个值得注意的设计细节
1.getPathForFile不走 IPC。它直接使用 Electron 的webUtils.getPathForFile(file)在本地解析File对象对应的真实路径,无需跨进程往返,这也是 Electron 官方推荐的替代file.path的写法。
2. 下载业务与平台能力的边界。源码注释明确说明:getEnvPath是「特殊案例」——它属于 GoApi 范畴,但因为在 Go 适配器初始化之前就需要用它发现coreUrl,所以 preload 中也保留了一份(见 src/index.ts)。其余所有数据操作仍走 Go Core HTTP,这让下载/转换等重型业务保持在服务端核心中,Electron 层只负责「桌面宿主能力」这一小部分。
通道常量:主进程与渲染进程的契约
所有 IPC 通道名集中定义在packages/common/src/constants/events.ts,使用带命名空间的字符串(如browser.createTab、dialog.open、update.check),并分为两类:
IPC:渲染进程 → 主进程的invoke通道(renderer → main);IpcEvent:主进程 → 渲染进程的事件推送(main → renderer),例如browser:tabsChanged(标签页列表变化)、browser:sourceDetected(嗅探到视频资源)、update:stateChanged(更新状态变化)、app:shareIntentAvailable(分享意图到达)、config:changed(配置变更)。
preload 中的每一个方法都直接引用IPC.*常量而非手写字符串:
createTab(options?: CreateBrowserTabInput): Promise<BrowserTabSnapshot> { return ipcRenderer.invoke(IPC.browser.createTab, options); }这种「常量 + 类型」双保险让主进程 handler 与 preload 调用天然同构:只要IPC常量变更,两侧编译器都会立刻报错,杜绝了手写字符串拼写不一致导致的运行时静默失败。
类型安全:PlatformApi与MediaGoApi
PlatformApi接口完整定义于packages/common/src/types/index.ts(约 第 564 行),preload 中声明的electronApi正是该接口的实现,并由:
export { electronApi }; export type { PlatformApi };导出,供测试与类型消费。同时,仓库还定义了组合类型:
export type MediaGoApi = GoApi & PlatformApi;即渲染进程全局 API 是「Go Core HTTP 业务 API + Electron 平台 API」的向后兼容并集——这为 UI 层(apps/electron与ui中通过core-sdk封装的客户端)提供了单一、完整的类型视图。
以browser命名空间为例,接口中多数方法接受可选tabId,并刻意设计成「老接口兼容迁移」形态:例如loadURL(tabIdOrUrl, url?)和setBounds(tabIdOrRect, rect?),首参为字符串时视为tabId,否则视为直接数据。preload 实现据此做参数归一化(见 src/index.ts),确保单标签页时代与多标签页迁移期调用方都能工作。
事件订阅:主进程 → 渲染进程的推送
除invoke请求/响应外,preload 还暴露了on/off两个订阅原语:
on(channel: string, listener: (...args: unknown[]) => void): void { ipcRenderer.on(channel, listener); }, off(channel: string, listener: (...args: unknown[]) => void): void { ipcRenderer.removeListener(channel, listener); },配合IpcEvent通道(如update:downloadProgress、browser:tabsChanged),渲染进程可以被动接收来自主进程的事件流,而不必轮询。这是下载进度、标签页列表同步、资源嗅探结果等「主动推送」场景的基础设施。
构建与开发流程
包使用tsdown(基于 Rolldown)构建,配置见 tsdown.config.ts:
- 输出目录
build,格式cjs,platform: "browser"; - 依赖打包策略
alwaysBundle: [/.*/]配合neverBundle: ["electron"]:将@mediago/common等内部依赖内联进产物,同时保留electron为运行时外部依赖; - 开启
minify与sourcemap,兼顾产物体积与调试。
开发命令(见 package.json):
# 构建包 pnpm build # 监听模式(开发) pnpm dev # 类型检查 pnpm type:check注意 README 中列出的pnpm types对应本仓库 package.json 中的type:check脚本,即tsc --noEmit,并配合oxlint做 lint 检查。
测试验证:IPC 契约的守护
src/index.test.ts使用 Vitest 对 preload 行为做了两层验证:
- Mock Electron 模块:用
vi.mock("electron", ...)替换contextBridge.exposeInMainWorld与ipcRenderer,从而无需真实启动 Electron 即可单测; - 断言调用参数:例如调用
createTab({ url: "https://example.com" })后,断言ipcRenderer.invoke收到的第一个参数是IPC.browser.createTab,第二个参数是{ url: ... };再如loadURL("tab-a", url)与旧的单参形式,断言它们被分别归一化为{ tabId: "tab-a", url }与{ tabId: "", url }。
这类测试将「通道常量是否一致」「参数形状是否正确」「旧接口兼容分支是否生效」固化下来,任何一侧的破坏都会在 CI 中暴露——这正是 preload 作为主进程/渲染进程契约层的价值所在。
在 Electron 主进程中的实际接线
在真实应用中,preload 产物路径通过包解析获得,而非硬编码:
// apps/electron/src/utils/index.ts const require = createRequire(import.meta.url); export const preloadUrl = require.resolve("@mediago/electron-preload");随后由窗口/标签创建逻辑注入webPreferences.preload(见apps/electron/src/services/browser-tab-manager.service.ts)。这样既避免了路径魔法,也保证构建产物位置变化时无需改动源码。
小结
@mediago/electron-preload是 MediaGo 桌面端安全边界与扩展点的交汇处:它以contextBridge暴露最小特权 API、以IPC常量与PlatformApi类型锁定主进程/渲染进程契约、以tsdown产出面向 Electron 的优化 CJS 产物,并用 Vitest 守护全部调用形状。对于任何希望理解或复刻「安全 Electron 桥接层」的开发者而言,这个包是一个结构清晰、边界明确的参考实现——业务走 HTTP、平台能力走 IPC,各司其职。
- 音视频
- 桌面应用
- 后端
【免费下载链接】mediago
跨平台视频提取工具:支持流媒体下载、视频下载、m3u8 下载及 B站视频下载,提供 Windows 和 Mac 桌面客户端。Cross-platform video extraction tool: Supports streaming download, video download, m3u8 download, and Bilibili video download, with desktop clients for Windows and Mac.
相关推荐
LobeHub 桌面端跨进程通信揭秘:@lobechat/electron-server-ipc 设计与实战
LobeHub 桌面端跨进程通信揭秘:@lobechat/electron server ipc 设计与实战 @lobechat/electron server
人工智能AI 应用大模型AI Agent多智能体工具调用前端后端shadPS4 快速上手:在 PC 上模拟 PS4 游戏的完整指南
shadPS4 快速上手:在 PC 上模拟 PS4 游戏的完整指南 想在 PC 上跑《血源诅咒》,又不想再掏钱买台 PS4?shadPS4 是款开源的 PS4
虚拟化图形学用 Netty 实践分布式 IM 即时通信系统:基于 JavaFx + SpringBoot + DDD 四层架构的仿微信桌面端全栈设计
用 Netty 实践分布式 IM 即时通信系统:基于 JavaFx + SpringBoot + DDD 四层架构的仿微信桌面端全栈设计 导读 本文以 Code
文档教程后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考