@rendiv/renderer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/browser.d.ts +9 -0
  2. package/dist/browser.d.ts.map +1 -0
  3. package/dist/browser.js +36 -0
  4. package/dist/browser.js.map +1 -0
  5. package/dist/cancel-signal.d.ts +6 -0
  6. package/dist/cancel-signal.d.ts.map +1 -0
  7. package/dist/cancel-signal.js +8 -0
  8. package/dist/cancel-signal.js.map +1 -0
  9. package/dist/get-compositions.d.ts +4 -0
  10. package/dist/get-compositions.d.ts.map +1 -0
  11. package/dist/get-compositions.js +57 -0
  12. package/dist/get-compositions.js.map +1 -0
  13. package/dist/index.d.ts +9 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +8 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/render-frames.d.ts +17 -0
  18. package/dist/render-frames.d.ts.map +1 -0
  19. package/dist/render-frames.js +71 -0
  20. package/dist/render-frames.js.map +1 -0
  21. package/dist/render-media.d.ts +18 -0
  22. package/dist/render-media.d.ts.map +1 -0
  23. package/dist/render-media.js +59 -0
  24. package/dist/render-media.js.map +1 -0
  25. package/dist/render-still.d.ts +13 -0
  26. package/dist/render-still.d.ts.map +1 -0
  27. package/dist/render-still.js +42 -0
  28. package/dist/render-still.js.map +1 -0
  29. package/dist/serve.d.ts +7 -0
  30. package/dist/serve.d.ts.map +1 -0
  31. package/dist/serve.js +70 -0
  32. package/dist/serve.js.map +1 -0
  33. package/dist/stitch-frames-to-video.d.ts +11 -0
  34. package/dist/stitch-frames-to-video.d.ts.map +1 -0
  35. package/dist/stitch-frames-to-video.js +40 -0
  36. package/dist/stitch-frames-to-video.js.map +1 -0
  37. package/dist/types.d.ts +10 -0
  38. package/dist/types.d.ts.map +1 -0
  39. package/dist/types.js +2 -0
  40. package/dist/types.js.map +1 -0
  41. package/package.json +32 -0
@@ -0,0 +1,9 @@
1
+ import { type Browser, type Page } from 'playwright';
2
+ export declare function openBrowser(): Promise<Browser>;
3
+ export declare function closeBrowser(): Promise<void>;
4
+ export declare function ensureBrowser(): Promise<Browser>;
5
+ export declare function openPage(browser: Browser, url: string, viewport: {
6
+ width: number;
7
+ height: number;
8
+ }): Promise<Page>;
9
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAI/D,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAcpD;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAKlD;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAEtD;AAED,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1C,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -0,0 +1,36 @@
1
+ import { chromium } from 'playwright';
2
+ let browserInstance = null;
3
+ export async function openBrowser() {
4
+ if (browserInstance && browserInstance.isConnected()) {
5
+ return browserInstance;
6
+ }
7
+ browserInstance = await chromium.launch({
8
+ headless: true,
9
+ args: [
10
+ '--disable-web-security',
11
+ '--disable-features=IsolateOrigins',
12
+ '--disable-site-isolation-trials',
13
+ '--no-sandbox',
14
+ ],
15
+ });
16
+ return browserInstance;
17
+ }
18
+ export async function closeBrowser() {
19
+ if (browserInstance) {
20
+ await browserInstance.close();
21
+ browserInstance = null;
22
+ }
23
+ }
24
+ export async function ensureBrowser() {
25
+ return openBrowser();
26
+ }
27
+ export async function openPage(browser, url, viewport) {
28
+ const context = await browser.newContext({
29
+ viewport,
30
+ deviceScaleFactor: 1,
31
+ });
32
+ const page = await context.newPage();
33
+ await page.goto(url, { waitUntil: 'networkidle' });
34
+ return page;
35
+ }
36
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAA2B,MAAM,YAAY,CAAC;AAE/D,IAAI,eAAe,GAAmB,IAAI,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,eAAe,IAAI,eAAe,CAAC,WAAW,EAAE,EAAE,CAAC;QACrD,OAAO,eAAe,CAAC;IACzB,CAAC;IACD,eAAe,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;QACtC,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE;YACJ,wBAAwB;YACxB,mCAAmC;YACnC,iCAAiC;YACjC,cAAc;SACf;KACF,CAAC,CAAC;IACH,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,IAAI,eAAe,EAAE,CAAC;QACpB,MAAM,eAAe,CAAC,KAAK,EAAE,CAAC;QAC9B,eAAe,GAAG,IAAI,CAAC;IACzB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,WAAW,EAAE,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,OAAgB,EAChB,GAAW,EACX,QAA2C;IAE3C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;QACvC,QAAQ;QACR,iBAAiB,EAAE,CAAC;KACrB,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IACrC,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;IACnD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface CancelSignal {
2
+ signal: AbortSignal;
3
+ cancel: () => void;
4
+ }
5
+ export declare function makeCancelSignal(): CancelSignal;
6
+ //# sourceMappingURL=cancel-signal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cancel-signal.d.ts","sourceRoot":"","sources":["../src/cancel-signal.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,wBAAgB,gBAAgB,IAAI,YAAY,CAM/C"}
@@ -0,0 +1,8 @@
1
+ export function makeCancelSignal() {
2
+ const controller = new AbortController();
3
+ return {
4
+ signal: controller.signal,
5
+ cancel: () => controller.abort(),
6
+ };
7
+ }
8
+ //# sourceMappingURL=cancel-signal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cancel-signal.js","sourceRoot":"","sources":["../src/cancel-signal.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,gBAAgB;IAC9B,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,OAAO;QACL,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE;KACjC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { CompositionInfo } from './types.js';
2
+ export declare function getCompositions(serveUrl: string): Promise<CompositionInfo[]>;
3
+ export declare function selectComposition(serveUrl: string, id: string, inputProps?: Record<string, unknown>): Promise<CompositionInfo>;
4
+ //# sourceMappingURL=get-compositions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-compositions.d.ts","sourceRoot":"","sources":["../src/get-compositions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CA6ClF;AAED,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,EACV,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,OAAO,CAAC,eAAe,CAAC,CAa1B"}
@@ -0,0 +1,57 @@
1
+ import { openBrowser, openPage } from './browser.js';
2
+ import { startServer } from './serve.js';
3
+ export async function getCompositions(serveUrl) {
4
+ const server = await startServer(serveUrl);
5
+ const browser = await openBrowser();
6
+ try {
7
+ const page = await openPage(browser, server.url, {
8
+ width: 1920,
9
+ height: 1080,
10
+ });
11
+ // Capture console errors for debugging
12
+ const errors = [];
13
+ page.on('console', (msg) => {
14
+ if (msg.type() === 'error') {
15
+ errors.push(msg.text());
16
+ }
17
+ });
18
+ page.on('pageerror', (err) => {
19
+ errors.push(err.message);
20
+ });
21
+ // Wait for the entry to load
22
+ try {
23
+ await page.waitForFunction(() => window.__RENDIV_LOADED__ === true, {
24
+ timeout: 30000,
25
+ });
26
+ }
27
+ catch (e) {
28
+ const errorDetails = errors.length > 0
29
+ ? `\nBrowser errors:\n${errors.join('\n')}`
30
+ : '\nNo browser errors captured.';
31
+ throw new Error(`Timed out waiting for Rendiv bundle to load.${errorDetails}`);
32
+ }
33
+ // Small delay to ensure compositions are registered
34
+ await page.waitForTimeout(100);
35
+ const compositions = await page.evaluate(() => {
36
+ return window.__RENDIV_GET_COMPOSITIONS__();
37
+ });
38
+ await page.close();
39
+ return compositions;
40
+ }
41
+ finally {
42
+ server.close();
43
+ }
44
+ }
45
+ export async function selectComposition(serveUrl, id, inputProps) {
46
+ const compositions = await getCompositions(serveUrl);
47
+ const comp = compositions.find((c) => c.id === id);
48
+ if (!comp) {
49
+ const available = compositions.map((c) => c.id).join(', ');
50
+ throw new Error(`Composition "${id}" not found. Available: ${available || '(none)'}`);
51
+ }
52
+ if (inputProps) {
53
+ comp.defaultProps = { ...comp.defaultProps, ...inputProps };
54
+ }
55
+ return comp;
56
+ }
57
+ //# sourceMappingURL=get-compositions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-compositions.js","sourceRoot":"","sources":["../src/get-compositions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB;IACpD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IAEpC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,EAAE;YAC/C,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;QAEH,uCAAuC;QACvC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;YACzB,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;gBAC3B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE;YAC3B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,6BAA6B;QAC7B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,CAAE,MAAc,CAAC,iBAAiB,KAAK,IAAI,EAAE;gBAC3E,OAAO,EAAE,KAAK;aACf,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC;gBACpC,CAAC,CAAC,sBAAsB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBAC3C,CAAC,CAAC,+BAA+B,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,+CAA+C,YAAY,EAAE,CAAC,CAAC;QACjF,CAAC;QAED,oDAAoD;QACpD,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QAE/B,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;YAC5C,OAAQ,MAAc,CAAC,2BAA2B,EAAE,CAAC;QACvD,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACnB,OAAO,YAAY,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAAgB,EAChB,EAAU,EACV,UAAoC;IAEpC,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAkB,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACpE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,SAAS,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAkB,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5E,MAAM,IAAI,KAAK,CACb,gBAAgB,EAAE,2BAA2B,SAAS,IAAI,QAAQ,EAAE,CACrE,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,CAAC,YAAY,GAAG,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,GAAG,UAAU,EAAE,CAAC;IAC9D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { renderMedia, type RenderMediaOptions } from './render-media.js';
2
+ export { renderFrames, type RenderFramesOptions } from './render-frames.js';
3
+ export { renderStill, type RenderStillOptions } from './render-still.js';
4
+ export { stitchFramesToVideo, type StitchOptions } from './stitch-frames-to-video.js';
5
+ export { getCompositions, selectComposition } from './get-compositions.js';
6
+ export { openBrowser, closeBrowser, ensureBrowser } from './browser.js';
7
+ export { makeCancelSignal, type CancelSignal } from './cancel-signal.js';
8
+ export type { CompositionInfo } from './types.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACzE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { renderMedia } from './render-media.js';
2
+ export { renderFrames } from './render-frames.js';
3
+ export { renderStill } from './render-still.js';
4
+ export { stitchFramesToVideo } from './stitch-frames-to-video.js';
5
+ export { getCompositions, selectComposition } from './get-compositions.js';
6
+ export { openBrowser, closeBrowser, ensureBrowser } from './browser.js';
7
+ export { makeCancelSignal } from './cancel-signal.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,YAAY,EAA4B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAsB,MAAM,6BAA6B,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAqB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { CompositionInfo } from './types.js';
2
+ export interface RenderFramesOptions {
3
+ serveUrl: string;
4
+ composition: CompositionInfo;
5
+ outputDir: string;
6
+ inputProps?: Record<string, unknown>;
7
+ concurrency?: number;
8
+ frameRange?: [number, number];
9
+ onFrameRendered?: (info: {
10
+ frame: number;
11
+ total: number;
12
+ }) => void;
13
+ cancelSignal?: AbortSignal;
14
+ timeoutPerFrame?: number;
15
+ }
16
+ export declare function renderFrames(options: RenderFramesOptions): Promise<void>;
17
+ //# sourceMappingURL=render-frames.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-frames.d.ts","sourceRoot":"","sources":["../src/render-frames.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,eAAe,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACnE,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AA4BD,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2E9E"}
@@ -0,0 +1,71 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { openBrowser, openPage } from './browser.js';
4
+ import { startServer } from './serve.js';
5
+ async function renderSingleFrame(page, frame, outputDir, timeoutPerFrame) {
6
+ // Set the frame and wait for it to be ready
7
+ await page.evaluate((f) => {
8
+ return window.__RENDIV_SET_FRAME__(f);
9
+ }, frame);
10
+ // Additional check for delay renders
11
+ await page.waitForFunction(() => window.__RENDIV_PENDING_HOLDS__() === 0, { timeout: timeoutPerFrame });
12
+ // Screenshot
13
+ const paddedFrame = String(frame).padStart(6, '0');
14
+ await page.screenshot({
15
+ path: path.join(outputDir, `frame-${paddedFrame}.png`),
16
+ type: 'png',
17
+ animations: 'disabled',
18
+ });
19
+ }
20
+ export async function renderFrames(options) {
21
+ const { serveUrl, composition, outputDir, inputProps = {}, concurrency = 1, frameRange, onFrameRendered, cancelSignal, timeoutPerFrame = 30000, } = options;
22
+ const [startFrame, endFrame] = frameRange ?? [0, composition.durationInFrames - 1];
23
+ const totalFrames = endFrame - startFrame + 1;
24
+ fs.mkdirSync(outputDir, { recursive: true });
25
+ const server = await startServer(serveUrl);
26
+ const browser = await openBrowser();
27
+ try {
28
+ // Create page pool
29
+ const pages = [];
30
+ for (let i = 0; i < concurrency; i++) {
31
+ const page = await openPage(browser, server.url, {
32
+ width: composition.width,
33
+ height: composition.height,
34
+ });
35
+ // Wait for entry to load
36
+ await page.waitForFunction(() => window.__RENDIV_LOADED__ === true, {
37
+ timeout: 30000,
38
+ });
39
+ // Set composition and input props
40
+ await page.evaluate(({ id, props }) => {
41
+ window.__RENDIV_SET_INPUT_PROPS__(props);
42
+ window.__RENDIV_SET_COMPOSITION__(id);
43
+ }, { id: composition.id, props: inputProps });
44
+ // Wait for composition to render
45
+ await page.waitForTimeout(100);
46
+ pages.push(page);
47
+ }
48
+ // Render frames using a simple pool
49
+ const frameQueue = Array.from({ length: totalFrames }, (_, i) => startFrame + i);
50
+ let completedFrames = 0;
51
+ const renderNext = async (page) => {
52
+ while (frameQueue.length > 0) {
53
+ if (cancelSignal?.aborted)
54
+ return;
55
+ const frame = frameQueue.shift();
56
+ await renderSingleFrame(page, frame, outputDir, timeoutPerFrame);
57
+ completedFrames++;
58
+ onFrameRendered?.({ frame: completedFrames, total: totalFrames });
59
+ }
60
+ };
61
+ await Promise.all(pages.map((page) => renderNext(page)));
62
+ // Cleanup pages
63
+ for (const page of pages) {
64
+ await page.close();
65
+ }
66
+ }
67
+ finally {
68
+ server.close();
69
+ }
70
+ }
71
+ //# sourceMappingURL=render-frames.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-frames.js","sourceRoot":"","sources":["../src/render-frames.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { CompositionInfo } from './types.js';
2
+ export interface RenderMediaOptions {
3
+ composition: CompositionInfo;
4
+ serveUrl: string;
5
+ codec?: 'mp4' | 'webm';
6
+ outputLocation: string;
7
+ inputProps?: Record<string, unknown>;
8
+ concurrency?: number;
9
+ onProgress?: (info: {
10
+ progress: number;
11
+ renderedFrames: number;
12
+ totalFrames: number;
13
+ }) => void;
14
+ frameRange?: [number, number];
15
+ cancelSignal?: AbortSignal;
16
+ }
17
+ export declare function renderMedia(options: RenderMediaOptions): Promise<void>;
18
+ //# sourceMappingURL=render-media.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-media.d.ts","sourceRoot":"","sources":["../src/render-media.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,eAAe,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/F,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,YAAY,CAAC,EAAE,WAAW,CAAC;CAC5B;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoE5E"}
@@ -0,0 +1,59 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+ import { renderFrames } from './render-frames.js';
5
+ import { stitchFramesToVideo } from './stitch-frames-to-video.js';
6
+ export async function renderMedia(options) {
7
+ const { composition, serveUrl, codec = 'mp4', outputLocation, inputProps, concurrency, onProgress, frameRange, cancelSignal, } = options;
8
+ const totalFrames = frameRange
9
+ ? frameRange[1] - frameRange[0] + 1
10
+ : composition.durationInFrames;
11
+ const tmpDir = path.join(os.tmpdir(), `rendiv-frames-${Date.now()}`);
12
+ fs.mkdirSync(tmpDir, { recursive: true });
13
+ // Ensure output directory exists
14
+ const outputDir = path.dirname(outputLocation);
15
+ fs.mkdirSync(outputDir, { recursive: true });
16
+ try {
17
+ // Step 1: Render frames
18
+ await renderFrames({
19
+ serveUrl,
20
+ composition,
21
+ outputDir: tmpDir,
22
+ inputProps,
23
+ concurrency,
24
+ frameRange,
25
+ onFrameRendered: ({ frame, total }) => {
26
+ onProgress?.({
27
+ progress: (frame / total) * 0.9, // 90% for frame rendering
28
+ renderedFrames: frame,
29
+ totalFrames: total,
30
+ });
31
+ },
32
+ cancelSignal,
33
+ });
34
+ if (cancelSignal?.aborted)
35
+ return;
36
+ // Step 2: Stitch frames to video
37
+ onProgress?.({
38
+ progress: 0.9,
39
+ renderedFrames: totalFrames,
40
+ totalFrames,
41
+ });
42
+ await stitchFramesToVideo({
43
+ framesDir: tmpDir,
44
+ outputPath: outputLocation,
45
+ fps: composition.fps,
46
+ codec,
47
+ });
48
+ onProgress?.({
49
+ progress: 1.0,
50
+ renderedFrames: totalFrames,
51
+ totalFrames,
52
+ });
53
+ }
54
+ finally {
55
+ // Cleanup temp frames
56
+ fs.rmSync(tmpDir, { recursive: true, force: true });
57
+ }
58
+ }
59
+ //# sourceMappingURL=render-media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-media.js","sourceRoot":"","sources":["../src/render-media.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,YAAY,EAA4B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAelE,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAA2B;IAC3D,MAAM,EACJ,WAAW,EACX,QAAQ,EACR,KAAK,GAAG,KAAK,EACb,cAAc,EACd,UAAU,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACV,YAAY,GACb,GAAG,OAAO,CAAC;IAEZ,MAAM,WAAW,GAAG,UAAU;QAC5B,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC;QACnC,CAAC,CAAC,WAAW,CAAC,gBAAgB,CAAC;IAEjC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACrE,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,iCAAiC;IACjC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC/C,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,IAAI,CAAC;QACH,wBAAwB;QACxB,MAAM,YAAY,CAAC;YACjB,QAAQ;YACR,WAAW;YACX,SAAS,EAAE,MAAM;YACjB,UAAU;YACV,WAAW;YACX,UAAU;YACV,eAAe,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE;gBACpC,UAAU,EAAE,CAAC;oBACX,QAAQ,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,GAAG,EAAE,0BAA0B;oBAC3D,cAAc,EAAE,KAAK;oBACrB,WAAW,EAAE,KAAK;iBACnB,CAAC,CAAC;YACL,CAAC;YACD,YAAY;SACb,CAAC,CAAC;QAEH,IAAI,YAAY,EAAE,OAAO;YAAE,OAAO;QAElC,iCAAiC;QACjC,UAAU,EAAE,CAAC;YACX,QAAQ,EAAE,GAAG;YACb,cAAc,EAAE,WAAW;YAC3B,WAAW;SACZ,CAAC,CAAC;QAEH,MAAM,mBAAmB,CAAC;YACxB,SAAS,EAAE,MAAM;YACjB,UAAU,EAAE,cAAc;YAC1B,GAAG,EAAE,WAAW,CAAC,GAAG;YACpB,KAAK;SACN,CAAC,CAAC;QAEH,UAAU,EAAE,CAAC;YACX,QAAQ,EAAE,GAAG;YACb,cAAc,EAAE,WAAW;YAC3B,WAAW;SACZ,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,sBAAsB;QACtB,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { CompositionInfo } from './types.js';
2
+ export interface RenderStillOptions {
3
+ serveUrl: string;
4
+ composition: CompositionInfo;
5
+ output: string;
6
+ frame?: number;
7
+ inputProps?: Record<string, unknown>;
8
+ imageFormat?: 'png' | 'jpeg';
9
+ quality?: number;
10
+ timeoutPerFrame?: number;
11
+ }
12
+ export declare function renderStill(options: RenderStillOptions): Promise<void>;
13
+ //# sourceMappingURL=render-still.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-still.d.ts","sourceRoot":"","sources":["../src/render-still.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,eAAe,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4D5E"}
@@ -0,0 +1,42 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { openBrowser, openPage } from './browser.js';
4
+ import { startServer } from './serve.js';
5
+ export async function renderStill(options) {
6
+ const { serveUrl, composition, output, frame = 0, inputProps = {}, imageFormat = 'png', quality, timeoutPerFrame = 30000, } = options;
7
+ // Ensure output directory exists
8
+ const outputDir = path.dirname(output);
9
+ fs.mkdirSync(outputDir, { recursive: true });
10
+ const server = await startServer(serveUrl);
11
+ const browser = await openBrowser();
12
+ try {
13
+ const page = await openPage(browser, server.url, {
14
+ width: composition.width,
15
+ height: composition.height,
16
+ });
17
+ await page.waitForFunction(() => window.__RENDIV_LOADED__ === true, {
18
+ timeout: 30000,
19
+ });
20
+ await page.evaluate(({ id, props }) => {
21
+ window.__RENDIV_SET_INPUT_PROPS__(props);
22
+ window.__RENDIV_SET_COMPOSITION__(id);
23
+ }, { id: composition.id, props: inputProps });
24
+ await page.waitForTimeout(100);
25
+ // Set frame
26
+ await page.evaluate((f) => {
27
+ return window.__RENDIV_SET_FRAME__(f);
28
+ }, frame);
29
+ await page.waitForFunction(() => window.__RENDIV_PENDING_HOLDS__() === 0, { timeout: timeoutPerFrame });
30
+ await page.screenshot({
31
+ path: output,
32
+ type: imageFormat,
33
+ quality: imageFormat === 'jpeg' ? (quality ?? 80) : undefined,
34
+ animations: 'disabled',
35
+ });
36
+ await page.close();
37
+ }
38
+ finally {
39
+ server.close();
40
+ }
41
+ }
42
+ //# sourceMappingURL=render-still.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-still.js","sourceRoot":"","sources":["../src/render-still.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAczC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAA2B;IAC3D,MAAM,EACJ,QAAQ,EACR,WAAW,EACX,MAAM,EACN,KAAK,GAAG,CAAC,EACT,UAAU,GAAG,EAAE,EACf,WAAW,GAAG,KAAK,EACnB,OAAO,EACP,eAAe,GAAG,KAAK,GACxB,GAAG,OAAO,CAAC;IAEZ,iCAAiC;IACjC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IAEpC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,EAAE;YAC/C,KAAK,EAAE,WAAW,CAAC,KAAK;YACxB,MAAM,EAAE,WAAW,CAAC,MAAM;SAC3B,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,CAAE,MAAc,CAAC,iBAAiB,KAAK,IAAI,EAAE;YAC3E,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,QAAQ,CACjB,CAAC,EAAE,EAAE,EAAE,KAAK,EAAkD,EAAE,EAAE;YAC/D,MAAc,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC;YACjD,MAAc,CAAC,0BAA0B,CAAC,EAAE,CAAC,CAAC;QACjD,CAAC,EACD,EAAE,EAAE,EAAE,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAC1C,CAAC;QAEF,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QAE/B,YAAY;QACZ,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAS,EAAE,EAAE;YAChC,OAAQ,MAAc,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC;QACjD,CAAC,EAAE,KAAK,CAAC,CAAC;QAEV,MAAM,IAAI,CAAC,eAAe,CACxB,GAAG,EAAE,CAAE,MAAc,CAAC,wBAAwB,EAAE,KAAK,CAAC,EACtD,EAAE,OAAO,EAAE,eAAe,EAAE,CAC7B,CAAC;QAEF,MAAM,IAAI,CAAC,UAAU,CAAC;YACpB,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;YAC7D,UAAU,EAAE,UAAU;SACvB,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface ServeResult {
2
+ url: string;
3
+ port: number;
4
+ close: () => void;
5
+ }
6
+ export declare function startServer(rootDir: string, preferredPort?: number): Promise<ServeResult>;
7
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,SAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAoDpF"}
package/dist/serve.js ADDED
@@ -0,0 +1,70 @@
1
+ import http from 'node:http';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ const MIME_TYPES = {
5
+ '.html': 'text/html',
6
+ '.js': 'application/javascript',
7
+ '.mjs': 'application/javascript',
8
+ '.css': 'text/css',
9
+ '.json': 'application/json',
10
+ '.png': 'image/png',
11
+ '.jpg': 'image/jpeg',
12
+ '.jpeg': 'image/jpeg',
13
+ '.gif': 'image/gif',
14
+ '.svg': 'image/svg+xml',
15
+ '.webp': 'image/webp',
16
+ '.woff': 'font/woff',
17
+ '.woff2': 'font/woff2',
18
+ '.ttf': 'font/ttf',
19
+ '.otf': 'font/otf',
20
+ '.mp4': 'video/mp4',
21
+ '.webm': 'video/webm',
22
+ '.mp3': 'audio/mpeg',
23
+ '.wav': 'audio/wav',
24
+ };
25
+ export function startServer(rootDir, preferredPort = 0) {
26
+ return new Promise((resolve, reject) => {
27
+ const server = http.createServer((req, res) => {
28
+ const urlPath = decodeURIComponent(req.url ?? '/');
29
+ let filePath = path.join(rootDir, urlPath === '/' ? 'index.html' : urlPath);
30
+ // Try with .html extension if no extension
31
+ if (!path.extname(filePath) && !fs.existsSync(filePath)) {
32
+ filePath += '.html';
33
+ }
34
+ if (!fs.existsSync(filePath)) {
35
+ res.writeHead(404);
36
+ res.end('Not Found');
37
+ return;
38
+ }
39
+ const stat = fs.statSync(filePath);
40
+ if (stat.isDirectory()) {
41
+ filePath = path.join(filePath, 'index.html');
42
+ if (!fs.existsSync(filePath)) {
43
+ res.writeHead(404);
44
+ res.end('Not Found');
45
+ return;
46
+ }
47
+ }
48
+ const ext = path.extname(filePath).toLowerCase();
49
+ const contentType = MIME_TYPES[ext] || 'application/octet-stream';
50
+ res.writeHead(200, {
51
+ 'Content-Type': contentType,
52
+ 'Access-Control-Allow-Origin': '*',
53
+ 'Cache-Control': 'no-cache',
54
+ });
55
+ const stream = fs.createReadStream(filePath);
56
+ stream.pipe(res);
57
+ });
58
+ server.on('error', reject);
59
+ server.listen(preferredPort, '127.0.0.1', () => {
60
+ const address = server.address();
61
+ const port = typeof address === 'object' && address ? address.port : 0;
62
+ resolve({
63
+ url: `http://127.0.0.1:${port}`,
64
+ port,
65
+ close: () => server.close(),
66
+ });
67
+ });
68
+ });
69
+ }
70
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface StitchOptions {
2
+ framesDir: string;
3
+ outputPath: string;
4
+ fps: number;
5
+ codec?: 'mp4' | 'webm';
6
+ crf?: number;
7
+ pixelFormat?: string;
8
+ onProgress?: (progress: number) => void;
9
+ }
10
+ export declare function stitchFramesToVideo(options: StitchOptions): Promise<void>;
11
+ //# sourceMappingURL=stitch-frames-to-video.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stitch-frames-to-video.d.ts","sourceRoot":"","sources":["../src/stitch-frames-to-video.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD/E"}
@@ -0,0 +1,40 @@
1
+ import { spawn } from 'node:child_process';
2
+ import path from 'node:path';
3
+ // @ts-ignore - @ffmpeg-installer/ffmpeg has no types
4
+ import ffmpegInstaller from '@ffmpeg-installer/ffmpeg';
5
+ const ffmpegPath = ffmpegInstaller.path;
6
+ export async function stitchFramesToVideo(options) {
7
+ const { framesDir, outputPath, fps, codec = 'mp4', crf = 18, pixelFormat = 'yuv420p', } = options;
8
+ const inputPattern = path.join(framesDir, 'frame-%06d.png');
9
+ const args = [
10
+ '-y',
11
+ '-framerate', String(fps),
12
+ '-i', inputPattern,
13
+ ];
14
+ if (codec === 'mp4') {
15
+ args.push('-c:v', 'libx264', '-crf', String(crf), '-pix_fmt', pixelFormat, '-movflags', '+faststart');
16
+ }
17
+ else {
18
+ args.push('-c:v', 'libvpx-vp9', '-crf', String(crf), '-b:v', '0');
19
+ }
20
+ args.push(outputPath);
21
+ return new Promise((resolve, reject) => {
22
+ const proc = spawn(ffmpegPath, args, { stdio: ['ignore', 'pipe', 'pipe'] });
23
+ let stderrData = '';
24
+ proc.stderr.on('data', (data) => {
25
+ stderrData += data.toString();
26
+ });
27
+ proc.on('close', (code) => {
28
+ if (code === 0) {
29
+ resolve();
30
+ }
31
+ else {
32
+ reject(new Error(`FFmpeg exited with code ${code}:\n${stderrData}`));
33
+ }
34
+ });
35
+ proc.on('error', (err) => {
36
+ reject(new Error(`Failed to spawn FFmpeg: ${err.message}`));
37
+ });
38
+ });
39
+ }
40
+ //# sourceMappingURL=stitch-frames-to-video.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stitch-frames-to-video.js","sourceRoot":"","sources":["../src/stitch-frames-to-video.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,qDAAqD;AACrD,OAAO,eAAe,MAAM,0BAA0B,CAAC;AAEvD,MAAM,UAAU,GAAW,eAAe,CAAC,IAAI,CAAC;AAYhD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAsB;IAC9D,MAAM,EACJ,SAAS,EACT,UAAU,EACV,GAAG,EACH,KAAK,GAAG,KAAK,EACb,GAAG,GAAG,EAAE,EACR,WAAW,GAAG,SAAS,GACxB,GAAG,OAAO,CAAC;IAEZ,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;IAE5D,MAAM,IAAI,GAAa;QACrB,IAAI;QACJ,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC;QACzB,IAAI,EAAE,YAAY;KACnB,CAAC;IAEF,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,IAAI,CACP,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EACnB,UAAU,EAAE,WAAW,EACvB,WAAW,EAAE,YAAY,CAC1B,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,IAAI,CACP,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EACnB,MAAM,EAAE,GAAG,CACZ,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAEtB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAE5E,IAAI,UAAU,GAAG,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YACtC,UAAU,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChC,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,OAAO,EAAE,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,IAAI,MAAM,UAAU,EAAE,CAAC,CAAC,CAAC;YACvE,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACvB,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,10 @@
1
+ export interface CompositionInfo {
2
+ id: string;
3
+ durationInFrames: number;
4
+ fps: number;
5
+ width: number;
6
+ height: number;
7
+ defaultProps: Record<string, unknown>;
8
+ type: 'composition' | 'still';
9
+ }
10
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,gBAAgB,EAAE,MAAM,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC;CAC/B"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@rendiv/renderer",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "dependencies": {
18
+ "@ffmpeg-installer/ffmpeg": "^1.1.0",
19
+ "playwright": "^1.49.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.0.0",
23
+ "typescript": "^5.7.0",
24
+ "@rendiv/tsconfig": "0.0.0"
25
+ },
26
+ "scripts": {
27
+ "build": "tsc",
28
+ "dev": "tsc --watch",
29
+ "typecheck": "tsc --noEmit",
30
+ "clean": "rm -rf dist"
31
+ }
32
+ }