@miraiclip/server-export
Server-side export for Miraiclip: run exportProject in headless Chrome from Node — same pipeline as the browser, preview/export parity by construction.
npm install @miraiclip/server-export| Version | 0.4.3 |
| Runs in | Node ≥ 18, with Chrome or Chromium |
| Exports | 19 |
| Guides | Server (Node) |
Functions
createExportSession
A persistent exporter: ONE headless Chrome + harness page kept warm across
calls, so an export costs an export — not a browser launch (~1-2s each that
exportProjectFile pays per call). Exports run through the exact same
in-page exportProject pipeline; one page runs one export at a time, so
calls queue. Built for batch rendering (@miraiclip/templates) and
export-on-demand services.
function createExportSession(options?: CreateExportSessionOptions): Promise<ExportSession>Guides: Server (Node), Roadmap, Batch rendering
createRenderSession
A persistent still-frame renderer: ONE headless Chrome + harness page kept
warm across calls, so rendering a frame costs a frame — not a browser
launch. Frames render through the exact export pipeline (same compositor,
decode path, and fonts), so a still is what that composition time will look
like in the exported file. Built for agent loops (@miraiclip/mcp's
preview_frame) and thumbnail generation.
function createRenderSession(options?: CreateRenderSessionOptions): Promise<RenderSession>Guides: Server (Node), Stills, Roadmap
exportProjectFile
Export a Miraiclip project document from Node: launches headless Chrome,
serves a harness page + the project's media on a loopback server, runs the
exact same exportProject the browser uses, and returns the encoded file.
MP4 needs a real Chrome (free Chromium has no H.264 encoder — it fails the up-front codec probe with a clear error); WebM works on any Chromium.
function exportProjectFile(doc: ProjectDocument, options: ExportProjectFileOptions): Promise<ExportProjectFileResult>Guides: Server (Node), Roadmap
launchBrowser
Launch order: explicit executablePath → MIRAICLIP_BROWSER env → the
machine's installed Chrome (Playwright's "chrome" channel). We depend on
playwright-core (no forced browser download at install); real Chrome is
the recommended runtime because it ships the proprietary H.264/AAC
encoders — free Chromium builds export WebM only.
function launchBrowser(options?: BrowserOptions): Promise<Browser>parseCliArgs
Pure and unit-testable: argv (after node + script) → validated options.
function parseCliArgs(argv: string[]): ParsedCliArgsresolveAssetSources
Decide where each asset's bytes come from. Pure given exists, so the
mapping rules are unit-testable: an explicit assets entry wins, remote
URLs pass through untouched, and anything else is treated as a local path
resolved against assetsDir. Local assets are rewritten to /assets/<id>
so the in-page pipeline fetches them from the harness's own origin (media
fetches are range requests — the static server must answer them).
function resolveAssetSources(doc: ProjectDocument, options: {
assets?: Record<string, string> | undefined;
assetsDir?: string | undefined;
exists: (filePath: string) => boolean;
}): ResolvedAssetsClasses
ServerExportAbortedError
Thrown when the export is cancelled via the AbortSignal.
Extends Error.
new ServerExportAbortedError()Guides: Server (Node)
Interfaces
BrowserOptions
| Member | Type | Description |
|---|---|---|
executablePath? | string | Path to a Chrome/Chromium executable. Highest priority. Any distribution works — system Chrome, Chrome for Testing, Playwright's Chromium — but only real Chrome builds carry the proprietary H.264/AAC encoders MP4 export needs; free Chromium is WebM-only. |
args? | string[] | Extra Chrome flags, appended after the defaults. |
swiftshader? | boolean | Render WebGL on SwiftShader (software GL). Defaults to true on Linux — servers rarely have a GPU, and Chrome's default ANGLE stalls on GPU-less readback — and false elsewhere. |
CreateExportSessionOptions
| Member | Type | Description |
|---|---|---|
assets? | Record<string, string> | Asset id → local file path (same contract as exportProjectFile). |
assetsDir? | string | Base directory for resolving relative asset src paths (default: cwd). |
browser? | BrowserOptions |
CreateRenderSessionOptions
| Member | Type | Description |
|---|---|---|
assets? | Record<string, string> | Asset id → local file path (same contract as exportProjectFile). |
assetsDir? | string | Base directory for resolving relative asset src paths (default: cwd). |
browser? | BrowserOptions |
ExportProjectFileOptions
| Member | Type | Description |
|---|---|---|
format | "mp4" | "webm" | |
quality? | "draft" | "standard" | "high" | { videoBitrate: number; } | |
fps? | number | Output frame rate (default: the project's fps). |
width? | number | Output size (default: the project's composition size). |
height? | number | |
range? | { startUs: number; endUs: number; } | Composition range in µs (default: the whole composition). |
assets? | Record<string, string> | Where each asset's media lives on THIS machine: asset id → local file path. Assets whose src is already an http(s) or data URL are fetched as-is and need no entry; every other asset must either have an entry here or a src that resolves to a file under assetsDir. |
assetsDir? | string | Base directory for resolving relative asset src paths (default: cwd). |
out? | string | Write the encoded file here (created/overwritten). With out set the encoded chunks STREAM from the browser to the file as they are produced — peak memory stays flat however long the output is, and bytes is not returned. Omit to get the whole file back in memory instead (fine for short outputs; the in-page → Node hop transiently costs ~2× the size). |
audioChunkSeconds? | number | Audio mix chunk length in seconds (default 60): audio mixes in bounded sequential chunks, so timeline length does not grow mix memory. |
signal? | AbortSignal | |
onProgress? | (progress: ServerExportProgress) => void | |
browser? | BrowserOptions |
ExportProjectFileResult
| Member | Type | Description |
|---|---|---|
bytes? | Uint8Array | The encoded file — absent when out was given (it streamed to disk). |
filePath? | string | Set when out was given: the absolute path written. |
bytesWritten? | number | Set when out was given: total bytes streamed into the file. |
ExportSession
| Member | Type | Description |
|---|---|---|
exportFile | (doc: ProjectDocument, options: ExportFileOptions) => Promise<ExportProjectFileResult> | Export one document — same options and result as exportProjectFile, without the browser launch. The document is passed per call, so a session outlives edits and serves any number of documents. |
close | () => Promise<void> |
ParsedCliArgs
| Member | Type | Description |
|---|---|---|
projectPath | string | |
options | ExportProjectFileOptions | |
quiet | boolean |
ProjectDocument
The document: everything that is part of the composition, serialized and covered by undo history.
| Member | Type | Description |
|---|---|---|
schemaVersion | 1 | |
settings | ProjectSettings | |
assets | Record<string, Asset> | |
tracks | Record<string, Track> | |
trackOrder | string[] | Render order: index 0 is the bottom layer. |
clips | Record<string, Clip> | |
transitions | Record<string, Transition> | Transitions between adjacent clips, keyed by id. |
Guides: Collaboration, Stills, Defining templates
RenderSession
| Member | Type | Description |
|---|---|---|
renderStill | (doc: ProjectDocument, options: RenderStillOptions) => Promise<Uint8Array> | Render one frame of doc as a PNG. The document is passed per call, so a session outlives edits — hand it the latest state every time. |
close | () => Promise<void> |
Guides: Stills
RenderStillOptions
| Member | Type | Description |
|---|---|---|
timeUs | number | Composition time to render, in microseconds. |
width? | number | Output size (default: the document's composition size). |
height? | number |
ServerExportProgress
Mirrors the renderer's ExportProgress — re-declared so the Node side has no runtime dependency on the renderer.
| Member | Type | Description |
|---|---|---|
phase | "audio" | "video" | "finalizing" | |
framesDone | number | |
totalFrames | number | |
audioMixedUs? | number | |
audioTotalUs? | number | |
bytesWritten? | number | Streamed exports (out set) only: bytes written to the file so far. framesDone / totalFrames is the true completion fraction — total output size is the encoder's decision and unknowable up front — but this gives live file size, MB/s, and a remaining-bytes estimate: bytesWritten × (totalFrames − framesDone) / framesDone. Output flushes in ~16MiB chunks, so short exports may read 0 until finalize. |
Types
ExportFileOptions
Per-export options: everything exportProjectFile takes minus the session-level fields.
type ExportFileOptions = Omit<ExportProjectFileOptions, "assets" | "assetsDir" | "browser">;@miraiclip/templates
Parameterized videos for Miraiclip: a template project + a data payload → a hydrated document → export. Batch and personalized video generation, with field schemas that double as validation, UI forms, and LLM tools.
@miraiclip/assistant
An AI editing assistant for Miraiclip: a vendor-neutral ChatModel contract (OpenAI first; any OpenAI-compatible server works), an agent loop that edits a forked project and lands each request as one undo step, high-level editing tools, and a server handler that keeps API keys off the browser.