在 Electron 中使用 Remotion
學習如何將 Remotion 整合到 Electron 桌面應用程式中,包括打包配置、二進位檔案管理和平台特定注意事項
使用官方範本作為參考。 你的最終設定將取決於你的打包工具、打包目標和平台特定需求。請參考官方 Electron 範本。
使用本頁面了解 Electron 整合的注意事項,而不是作為嚴格的設定指南。不同應用程式的結構、安裝程式、程式碼簽署、自動更新和打包掛勾各不相同。
官方 Electron 範本是推薦的基準範本。
使用範本作為參考
範本展示了一個使用 Electron Forge 和 Vite 的打包渲染流程:
- 將 Remotion 專案保留在
remotion/中 - 透過 IPC 從渲染器觸發渲染
- 在 Electron 主程序中執行
selectComposition()和renderMedia() - 在打包期間建構 Remotion bundle,而非在打包後的執行期建構
你的應用程式中確切的打包設定可能有所不同。
在主程序中渲染
渲染器不應直接呼叫 renderMedia()。
從 preload.ts 暴露一個小型 API,然後讓 Electron 主程序執行渲染。這可以讓瀏覽器下載、檔案系統存取、儲存對話框和打包二進位路徑遠離你的 UI 程式碼。
import { contextBridge, ipcRenderer } from 'electron';
// 暴露給渲染器的 API
contextBridge.exposeInMainWorld('remotionAPI', {
renderVideo: (options: {
compositionId: string;
inputProps: Record<string, unknown>;
outputPath: string;
}) => ipcRenderer.invoke('render-video', options),
onRenderProgress: (
callback: (progress: { percent: number; frame: number }) => void
) => {
ipcRenderer.on('render-progress', (_event, data) => callback(data));
return () => ipcRenderer.removeAllListeners('render-progress');
},
});import { app, BrowserWindow, ipcMain, dialog } from 'electron';
import { bundle } from '@remotion/bundler';
import { renderMedia, selectComposition } from '@remotion/renderer';
import path from 'path';
let mainWindow: BrowserWindow | null = null;
app.whenReady().then(() => {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
// 處理渲染請求
ipcMain.handle(
'render-video',
async (
_event,
options: {
compositionId: string;
inputProps: Record<string, unknown>;
outputPath: string;
}
) => {
try {
// 在開發環境中:動態建構 bundle
// 在生產環境中:使用預建構的 bundle
const bundleLocation =
process.env.NODE_ENV === 'development'
? await bundle({
entryPoint: path.resolve('./remotion/index.ts'),
})
: path.join(process.resourcesPath, 'remotion-bundle');
const composition = await selectComposition({
serveUrl: bundleLocation,
id: options.compositionId,
inputProps: options.inputProps,
});
await renderMedia({
composition,
serveUrl: bundleLocation,
codec: 'h264',
outputLocation: options.outputPath,
onProgress: ({ progress }) => {
mainWindow?.webContents.send('render-progress', {
percent: Math.round(progress * 100),
});
},
});
return { success: true, outputPath: options.outputPath };
} catch (error) {
return { success: false, error: (error as Error).message };
}
}
);
});開發環境與打包應用程式
開發環境
在開發環境中,通常按需呼叫 bundle(),以便立即獲取 composition 的更改。
import { bundle } from '@remotion/bundler';
// 開發環境:動態建構 bundle
const bundleLocation = await bundle({
entryPoint: path.resolve('./remotion/index.ts'),
webpackOverride: (config) => config,
});打包應用程式
在打包應用程式中,不要在執行時呼叫 bundle()。在打包步驟期間建構 Remotion bundle,並從應用程式載入預建構的目錄。
import { bundle } from '@remotion/bundler';
import { copyRecursive } from 'fs-extra';
import path from 'path';
// 在打包期間預建構 bundle
const bundleLocation = await bundle({
entryPoint: path.resolve('./remotion/index.ts'),
});
// 將 bundle 複製到資源目錄
await copyRecursive(
bundleLocation,
path.join('./app-build', 'resources', 'remotion-bundle')
);組合器二進位檔案
使用 binariesDirectory 來指定打包渲染的路徑,並將其指向 app.asar.unpacked 中的組合器套件。
Remotion 二進位檔案不得從 app.asar 內部載入。
import { renderMedia } from '@remotion/renderer';
import { app } from 'electron';
import path from 'path';
const binariesDirectory = app.isPackaged
? path.join(
process.resourcesPath,
'app.asar.unpacked',
'node_modules',
'@remotion',
`compositor-${process.platform}-${process.arch}`
)
: undefined; // 開發環境使用預設路徑
await renderMedia({
// ...
binariesDirectory,
});平台特定注意事項
瀏覽器下載
第一次渲染可能會下載 Chrome Headless Shell。
如果你的應用程式預期使用者在啟動後很快進行渲染,你可以提前呼叫 ensureBrowser(),例如在啟動後在背景呼叫。
import { ensureBrowser } from '@remotion/renderer';
app.whenReady().then(async () => {
// 在背景下載瀏覽器
ensureBrowser().catch(console.error);
// 繼續正常初始化...
createWindow();
});如果你想在打包版本中避免首次使用時下載,你也可以將瀏覽器視為打包資源。在打包期間呼叫 ensureBrowser(),將下載的瀏覽器複製到 app.asar.unpacked,並在打包版本的渲染中透過 browserExecutable 選項傳遞其路徑。
官方 Electron 範本預設禁用此功能。如果你想讓打包版本完全離線渲染,請在打包時設定
REMOTION_ELECTRON_PACKAGE_BROWSER=true。這將顯著增加最終應用程式的大小。
macOS 通用版本不支援此功能。
macOS 通用版本
通用打包需要在打包期間同時提供兩個 macOS 組合器套件(x64 和 arm64)。
如果你的打包流程從分離的 x64 和 arm64 應用程式包生成通用應用程式,通用合并步驟可能需要對特定架構的二進位檔案設置允許列表。Electron Forge 透過 packagerConfig.osxUniversal 來暴露此功能。
Linux 變體
在 Linux 上,除了 CPU 架構之外,你還可能需要針對基於 glibc 的發行版(如 Ubuntu 和 Debian)以及基於 musl 的發行版(如 Alpine)使用不同的二進位檔案。
Remotion 為這些環境提供了單獨的 Linux 組合器套件,例如 @remotion/compositor-linux-x64-gnu 和 @remotion/compositor-linux-x64-musl。
在打包期間,包含與你的目標 Linux 變體匹配的組合器套件,並將其放置到 app.asar.unpacked 中。
{
"optionalDependencies": {
"@remotion/compositor-linux-x64-gnu": "*",
"@remotion/compositor-linux-x64-musl": "*",
"@remotion/compositor-linux-arm64-gnu": "*"
}
}設定不同於範本的情況
Electron Forge 是一個很好的基準,但它不是唯一有效的設定。
你的應用程式可能使用 Electron Builder、不同的渲染器堆疊、自定義的預載 API 或不同的打包流程。重要的是要將 Remotion 渲染保留在主程序中,並明確指定打包的二進位路徑。