@open-motion/cli 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.
package/dist/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const index_1 = require("./index");
5
+ (0, index_1.main)();
@@ -0,0 +1,13 @@
1
+ export declare const runInit: (projectName: string) => Promise<void>;
2
+ export declare const runRender: (options: {
3
+ url: string;
4
+ out: string;
5
+ compositionId?: string;
6
+ width?: number;
7
+ height?: number;
8
+ fps?: number;
9
+ duration?: number;
10
+ props?: string;
11
+ concurrency?: number;
12
+ }) => Promise<void>;
13
+ export declare const main: () => void;
package/dist/index.js ADDED
@@ -0,0 +1,223 @@
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.main = exports.runRender = exports.runInit = void 0;
7
+ const renderer_1 = require("@open-motion/renderer");
8
+ const encoder_1 = require("@open-motion/encoder");
9
+ const path_1 = __importDefault(require("path"));
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const commander_1 = require("commander");
12
+ const runInit = async (projectName) => {
13
+ const targetDir = path_1.default.join(process.cwd(), projectName);
14
+ if (fs_1.default.existsSync(targetDir)) {
15
+ console.error(`Directory ${projectName} already exists.`);
16
+ process.exit(1);
17
+ }
18
+ console.log(`Initializing OpenMotion project: ${projectName}...`);
19
+ // Basic template structure
20
+ const dirs = ['', 'src'];
21
+ for (const dir of dirs) {
22
+ fs_1.default.mkdirSync(path_1.default.join(targetDir, dir), { recursive: true });
23
+ }
24
+ // Template files
25
+ const files = {
26
+ 'package.json': JSON.stringify({
27
+ name: projectName,
28
+ private: true,
29
+ version: '0.0.1-alpha.0',
30
+ type: 'module',
31
+ scripts: {
32
+ dev: 'vite',
33
+ build: 'vite build',
34
+ preview: 'vite preview',
35
+ render: 'open-motion render -u http://localhost:5173 -o out.mp4'
36
+ },
37
+ dependencies: {
38
+ 'react': '^18.2.0',
39
+ 'react-dom': '^18.2.0',
40
+ '@open-motion/core': 'latest'
41
+ },
42
+ devDependencies: {
43
+ '@vitejs/plugin-react': '^4.0.0',
44
+ 'vite': '^4.4.0',
45
+ 'typescript': '^5.0.0'
46
+ }
47
+ }, null, 2),
48
+ 'index.html': `<!DOCTYPE html>
49
+ <html lang="en">
50
+ <head>
51
+ <meta charset="UTF-8" />
52
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
53
+ <title>${projectName}</title>
54
+ </head>
55
+ <body>
56
+ <div id="root"></div>
57
+ <script type="module" src="/src/main.tsx"></script>
58
+ </body>
59
+ </html>`,
60
+ 'vite.config.ts': `import { defineConfig } from 'vite';
61
+ import react from '@vitejs/plugin-react';
62
+
63
+ export default defineConfig({
64
+ plugins: [react()],
65
+ });`,
66
+ 'src/main.tsx': `import React from 'react';
67
+ import ReactDOM from 'react-dom/client';
68
+ import { App } from './App.tsx';
69
+
70
+ ReactDOM.createRoot(document.getElementById('root')!).render(
71
+ <React.StrictMode>
72
+ <App />
73
+ </React.StrictMode>
74
+ );`,
75
+ 'src/App.tsx': `import React from 'react';
76
+ import {
77
+ CompositionProvider,
78
+ useCurrentFrame,
79
+ useVideoConfig,
80
+ interpolate,
81
+ Composition,
82
+ Player
83
+ } from '@open-motion/core';
84
+
85
+ const MyVideo = () => {
86
+ const frame = useCurrentFrame();
87
+ const { width, height } = useVideoConfig();
88
+ const opacity = interpolate(frame, [0, 30], [0, 1]);
89
+
90
+ return (
91
+ <div style={{
92
+ flex: 1,
93
+ backgroundColor: 'white',
94
+ width,
95
+ height,
96
+ display: 'flex',
97
+ justifyContent: 'center',
98
+ alignItems: 'center',
99
+ fontSize: 80,
100
+ opacity
101
+ }}>
102
+ Hello OpenMotion
103
+ </div>
104
+ );
105
+ };
106
+
107
+ export const App = () => {
108
+ const config = { width: 1280, height: 720, fps: 30, durationInFrames: 60 };
109
+ const isRendering = typeof (window as any).__OPEN_MOTION_FRAME__ === 'number';
110
+
111
+ if (isRendering) {
112
+ return (
113
+ <CompositionProvider config={config} frame={(window as any).__OPEN_MOTION_FRAME__}>
114
+ <MyVideo />
115
+ </CompositionProvider>
116
+ );
117
+ }
118
+
119
+ return (
120
+ <div style={{ padding: '20px' }}>
121
+ <Player component={MyVideo} config={config} />
122
+ <div style={{ display: 'none' }}>
123
+ <Composition id="main" component={MyVideo} {...config} />
124
+ </div>
125
+ </div>
126
+ );
127
+ };`
128
+ };
129
+ for (const [name, content] of Object.entries(files)) {
130
+ fs_1.default.writeFileSync(path_1.default.join(targetDir, name), content);
131
+ }
132
+ console.log(`Success! Project ${projectName} initialized.`);
133
+ console.log(`Next steps:`);
134
+ console.log(` cd ${projectName}`);
135
+ console.log(` npm install (or pnpm install)`);
136
+ console.log(` npm run dev`);
137
+ };
138
+ exports.runInit = runInit;
139
+ const runRender = async (options) => {
140
+ const tmpDir = path_1.default.join(process.cwd(), '.open-motion-tmp');
141
+ const inputProps = options.props ? JSON.parse(options.props) : {};
142
+ console.log(`Fetching compositions from ${options.url}...`);
143
+ const compositions = await (0, renderer_1.getCompositions)(options.url);
144
+ if (compositions.length === 0) {
145
+ console.error('No compositions found in the provided URL.');
146
+ process.exit(1);
147
+ }
148
+ let selectedComp = compositions[0];
149
+ if (options.compositionId) {
150
+ selectedComp = compositions.find((c) => c.id === options.compositionId);
151
+ if (!selectedComp) {
152
+ console.error(`Composition "${options.compositionId}" not found. Available: ${compositions.map((c) => c.id).join(', ')}`);
153
+ process.exit(1);
154
+ }
155
+ }
156
+ const config = {
157
+ width: options.width || selectedComp.width,
158
+ height: options.height || selectedComp.height,
159
+ fps: options.fps || selectedComp.fps,
160
+ durationInFrames: options.duration || selectedComp.durationInFrames
161
+ };
162
+ console.log(`Rendering composition: ${selectedComp.id} (${config.width}x${config.height}, ${config.fps}fps, ${config.durationInFrames} frames)`);
163
+ const { audioAssets } = await (0, renderer_1.renderFrames)({
164
+ url: options.url,
165
+ config,
166
+ outputDir: tmpDir,
167
+ compositionId: selectedComp.id,
168
+ inputProps,
169
+ concurrency: options.concurrency || 1
170
+ });
171
+ // Handle first audio asset for now (simplified)
172
+ const audioFile = audioAssets.length > 0 ? audioAssets[0].src : undefined;
173
+ await (0, encoder_1.encodeVideo)({
174
+ framesDir: tmpDir,
175
+ fps: config.fps,
176
+ outputFile: options.out,
177
+ audioFile
178
+ });
179
+ console.log(`Success! Video rendered to ${options.out}`);
180
+ };
181
+ exports.runRender = runRender;
182
+ const main = () => {
183
+ const program = new commander_1.Command();
184
+ program
185
+ .name('open-motion')
186
+ .description('CLI for OpenMotion')
187
+ .version('0.0.1-alpha.0');
188
+ program
189
+ .command('init <name>')
190
+ .description('Initialize a new OpenMotion project')
191
+ .action(async (name) => {
192
+ try {
193
+ await (0, exports.runInit)(name);
194
+ }
195
+ catch (err) {
196
+ console.error('Init failed:', err);
197
+ process.exit(1);
198
+ }
199
+ });
200
+ program
201
+ .command('render')
202
+ .description('Render a video')
203
+ .requiredOption('-u, --url <url>', 'URL of the OpenMotion app')
204
+ .requiredOption('-o, --out <path>', 'Output video file path')
205
+ .option('-c, --composition <id>', 'ID of the composition to render')
206
+ .option('-p, --props <json>', 'JSON string of props to pass to the composition')
207
+ .option('-j, --concurrency <number>', 'Number of parallel browser instances', parseInt)
208
+ .option('--width <number>', 'Override width', parseInt)
209
+ .option('--height <number>', 'Override height', parseInt)
210
+ .option('--fps <number>', 'Override FPS', parseInt)
211
+ .option('--duration <number>', 'Override duration in frames', parseInt)
212
+ .action(async (options) => {
213
+ try {
214
+ await (0, exports.runRender)(options);
215
+ }
216
+ catch (err) {
217
+ console.error('Render failed:', err);
218
+ process.exit(1);
219
+ }
220
+ });
221
+ program.parse(process.argv);
222
+ };
223
+ exports.main = main;
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@open-motion/cli",
3
+ "version": "0.0.1-alpha.0",
4
+ "bin": {
5
+ "open-motion": "dist/bin.js"
6
+ },
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/open-motion/open-motion.git",
11
+ "directory": "packages/cli"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "dependencies": {
20
+ "commander": "^11.0.0",
21
+ "@open-motion/renderer": "0.0.1-alpha.0",
22
+ "@open-motion/core": "0.0.1-alpha.0",
23
+ "@open-motion/encoder": "0.0.1-alpha.0"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc && chmod +x dist/bin.js"
27
+ }
28
+ }