@open-motion/renderer 0.0.1-alpha.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.
@@ -0,0 +1,13 @@
1
+ import { VideoConfig } from '@open-motion/core';
2
+ export interface RenderOptions {
3
+ url: string;
4
+ config: VideoConfig;
5
+ outputDir: string;
6
+ compositionId?: string;
7
+ inputProps?: any;
8
+ concurrency?: number;
9
+ }
10
+ export declare const getCompositions: (url: string) => Promise<any>;
11
+ export declare const renderFrames: ({ url, config, outputDir, compositionId, inputProps, concurrency }: RenderOptions) => Promise<{
12
+ audioAssets: any[];
13
+ }>;
package/dist/index.js ADDED
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.renderFrames = exports.getCompositions = void 0;
7
+ const playwright_1 = require("playwright");
8
+ const core_1 = require("@open-motion/core");
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const getCompositions = async (url) => {
12
+ const browser = await playwright_1.chromium.launch();
13
+ const page = await browser.newPage();
14
+ await page.goto(url);
15
+ await page.waitForLoadState('networkidle');
16
+ // Wait a bit for React to mount and compositions to register
17
+ await page.waitForFunction(() => window.__OPEN_MOTION_COMPOSITIONS__ !== undefined, { timeout: 5000 }).catch(() => { });
18
+ const compositions = await page.evaluate(() => {
19
+ return window.__OPEN_MOTION_COMPOSITIONS__ || [];
20
+ });
21
+ await browser.close();
22
+ return compositions;
23
+ };
24
+ exports.getCompositions = getCompositions;
25
+ const renderFrames = async ({ url, config, outputDir, compositionId, inputProps = {}, concurrency = 1 }) => {
26
+ if (!fs_1.default.existsSync(outputDir)) {
27
+ fs_1.default.mkdirSync(outputDir, { recursive: true });
28
+ }
29
+ const framesPerWorker = Math.ceil(config.durationInFrames / concurrency);
30
+ const audioAssets = [];
31
+ const renderBatch = async (startFrame, endFrame, workerId) => {
32
+ const browser = await playwright_1.chromium.launch({
33
+ args: ['--disable-dev-shm-usage', '--disable-setuid-sandbox', '--no-sandbox']
34
+ });
35
+ const page = await browser.newPage({
36
+ viewport: { width: config.width, height: config.height }
37
+ });
38
+ console.log(`Worker ${workerId}: Rendering frames ${startFrame} to ${endFrame}...`);
39
+ for (let i = startFrame; i <= endFrame && i < config.durationInFrames; i++) {
40
+ if (i === startFrame) {
41
+ await page.goto(url);
42
+ }
43
+ await page.evaluate(({ frame, fps, hijackScript, compositionId, inputProps }) => {
44
+ window.__OPEN_MOTION_FRAME__ = frame;
45
+ window.__OPEN_MOTION_COMPOSITION_ID__ = compositionId;
46
+ window.__OPEN_MOTION_INPUT_PROPS__ = inputProps;
47
+ eval(hijackScript);
48
+ }, {
49
+ frame: i,
50
+ fps: config.fps,
51
+ hijackScript: (0, core_1.getTimeHijackScript)(i, config.fps),
52
+ compositionId,
53
+ inputProps
54
+ });
55
+ await page.waitForFunction(() => !(window.__OPEN_MOTION_DELAY_RENDER_COUNT__ > 0), { timeout: 30000 });
56
+ await page.waitForLoadState('networkidle');
57
+ // Extract audio assets from the first frame or each frame
58
+ if (workerId === 0 && i === 0) {
59
+ const assets = await page.evaluate(() => window.__OPEN_MOTION_AUDIO_ASSETS__ || []);
60
+ audioAssets.push(...assets);
61
+ }
62
+ const screenshotPath = path_1.default.join(outputDir, `frame-${i.toString().padStart(5, '0')}.png`);
63
+ await page.screenshot({ path: screenshotPath, type: 'png' });
64
+ }
65
+ await browser.close();
66
+ };
67
+ const workers = [];
68
+ for (let i = 0; i < concurrency; i++) {
69
+ workers.push(renderBatch(i * framesPerWorker, (i + 1) * framesPerWorker - 1, i));
70
+ }
71
+ await Promise.all(workers);
72
+ console.log('Frame rendering complete.');
73
+ return { audioAssets };
74
+ };
75
+ exports.renderFrames = renderFrames;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const playwright_1 = require("playwright");
7
+ const child_process_1 = require("child_process");
8
+ const path_1 = __importDefault(require("path"));
9
+ const TEST_PORT = 3005;
10
+ const APP_URL = `http://localhost:${TEST_PORT}`;
11
+ async function runTest() {
12
+ console.log('🚀 Starting E2E Tests...');
13
+ // 1. Start dev server
14
+ const devServer = (0, child_process_1.spawn)('pnpm', ['dev', '--port', TEST_PORT.toString()], {
15
+ cwd: path_1.default.resolve(__dirname, '../../../examples/hello-world'),
16
+ shell: false
17
+ });
18
+ devServer.stdout.on('data', (data) => console.log(`[DevServer]: ${data}`));
19
+ // Wait for dev server to be ready
20
+ await new Promise(resolve => setTimeout(resolve, 5000));
21
+ const browser = await playwright_1.chromium.launch();
22
+ const page = await browser.newPage();
23
+ try {
24
+ // Scene 1: Basic Rendering & Hooks
25
+ console.log('Checking Scene 1: Hooks & Rendering...');
26
+ await page.goto(APP_URL);
27
+ await page.waitForSelector('h1');
28
+ const title = await page.innerText('h1');
29
+ if (title !== 'OpenMotion Player Preview')
30
+ throw new Error('Player Title mismatch');
31
+ console.log('✅ Scene 1 Passed');
32
+ // Scene 2: Input Props Injection
33
+ console.log('Checking Scene 2: Input Props...');
34
+ // We'll test this via CLI later, but check if default props work in Player
35
+ const welcomeText = await page.innerText('span');
36
+ if (!welcomeText.includes('Claude Preview'))
37
+ throw new Error('Input Props not reflected');
38
+ console.log('✅ Scene 2 Passed');
39
+ // Scene 3: delayRender
40
+ console.log('Checking Scene 3: delayRender...');
41
+ // Check if __OPEN_MOTION_DELAY_RENDER_COUNT__ exists and becomes 0
42
+ const delayCount = await page.evaluate(() => window.__OPEN_MOTION_DELAY_RENDER_COUNT__);
43
+ console.log(`Initial delay count: ${delayCount}`);
44
+ await page.waitForFunction(() => window.__OPEN_MOTION_DELAY_RENDER_COUNT__ === 0, { timeout: 10000 });
45
+ console.log('✅ Scene 3 Passed');
46
+ }
47
+ catch (err) {
48
+ console.error('❌ Test Failed:', err);
49
+ process.exit(1);
50
+ }
51
+ finally {
52
+ await browser.close();
53
+ devServer.kill();
54
+ }
55
+ console.log('🎉 All E2E Tests Passed!');
56
+ process.exit(0);
57
+ }
58
+ runTest();
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@open-motion/renderer",
3
+ "version": "0.0.1-alpha.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/open-motion/open-motion.git",
10
+ "directory": "packages/renderer"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "dependencies": {
19
+ "playwright": "^1.40.0",
20
+ "@open-motion/core": "0.0.1-alpha.0"
21
+ },
22
+ "scripts": {
23
+ "build": "tsc"
24
+ }
25
+ }