@open-motion/cli 0.0.1-alpha.0 → 0.0.2

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 (3) hide show
  1. package/README.md +32 -0
  2. package/dist/index.js +63 -9
  3. package/package.json +9 -5
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # @open-motion/cli
2
+
3
+ The Command Line Interface for **OpenMotion** — the open-source programmatic video engine.
4
+
5
+ ## 🚀 Features
6
+
7
+ - 🏗️ **Quick Init**: Scaffold new OpenMotion projects instantly.
8
+ - 🎥 **High-Speed Rendering**: Capture and encode videos directly from your React code.
9
+ - 🚀 **Parallel Execution**: Leverage multi-core CPUs for faster frame capturing.
10
+ - 🎛️ **Dynamic Props**: Inject external data into your videos via JSON.
11
+
12
+ ## 🛠 Installation
13
+
14
+ ```bash
15
+ npm install -g @open-motion/cli
16
+ # or use via npx
17
+ npx @open-motion/cli --help
18
+ ```
19
+
20
+ ## 📖 Usage
21
+
22
+ ### Initialize a project
23
+ ```bash
24
+ open-motion init my-video
25
+ ```
26
+
27
+ ### Render a video
28
+ ```bash
29
+ open-motion render --url http://localhost:5173 --out out.mp4 --concurrency 4
30
+ ```
31
+
32
+ Learn more at the [main OpenMotion repository](https://github.com/jsongo/open-motion).
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ const encoder_1 = require("@open-motion/encoder");
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const fs_1 = __importDefault(require("fs"));
11
11
  const commander_1 = require("commander");
12
+ const cli_progress_1 = __importDefault(require("cli-progress"));
12
13
  const runInit = async (projectName) => {
13
14
  const targetDir = path_1.default.join(process.cwd(), projectName);
14
15
  if (fs_1.default.existsSync(targetDir)) {
@@ -50,7 +51,7 @@ const runInit = async (projectName) => {
50
51
  <head>
51
52
  <meta charset="UTF-8" />
52
53
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
53
- <title>${projectName}</title>
54
+ <title>\${projectName}</title>
54
55
  </head>
55
56
  <body>
56
57
  <div id="root"></div>
@@ -59,9 +60,17 @@ const runInit = async (projectName) => {
59
60
  </html>`,
60
61
  'vite.config.ts': `import { defineConfig } from 'vite';
61
62
  import react from '@vitejs/plugin-react';
63
+ import path from 'path';
62
64
 
63
65
  export default defineConfig({
64
66
  plugins: [react()],
67
+ resolve: {
68
+ alias: {
69
+ 'react': path.resolve(__dirname, 'node_modules/react'),
70
+ 'react-dom': path.resolve(__dirname, 'node_modules/react-dom'),
71
+ },
72
+ dedupe: ['react', 'react-dom'],
73
+ },
65
74
  });`,
66
75
  'src/main.tsx': `import React from 'react';
67
76
  import ReactDOM from 'react-dom/client';
@@ -129,9 +138,9 @@ export const App = () => {
129
138
  for (const [name, content] of Object.entries(files)) {
130
139
  fs_1.default.writeFileSync(path_1.default.join(targetDir, name), content);
131
140
  }
132
- console.log(`Success! Project ${projectName} initialized.`);
141
+ console.log(`Success! Project \${projectName} initialized.`);
133
142
  console.log(`Next steps:`);
134
- console.log(` cd ${projectName}`);
143
+ console.log(` cd \${projectName}`);
135
144
  console.log(` npm install (or pnpm install)`);
136
145
  console.log(` npm run dev`);
137
146
  };
@@ -139,6 +148,7 @@ exports.runInit = runInit;
139
148
  const runRender = async (options) => {
140
149
  const tmpDir = path_1.default.join(process.cwd(), '.open-motion-tmp');
141
150
  const inputProps = options.props ? JSON.parse(options.props) : {};
151
+ const startTime = Date.now();
142
152
  console.log(`Fetching compositions from ${options.url}...`);
143
153
  const compositions = await (0, renderer_1.getCompositions)(options.url);
144
154
  if (compositions.length === 0) {
@@ -160,23 +170,57 @@ const runRender = async (options) => {
160
170
  durationInFrames: options.duration || selectedComp.durationInFrames
161
171
  };
162
172
  console.log(`Rendering composition: ${selectedComp.id} (${config.width}x${config.height}, ${config.fps}fps, ${config.durationInFrames} frames)`);
173
+ const multibar = new cli_progress_1.default.MultiBar({
174
+ clearOnComplete: false,
175
+ hideCursor: true,
176
+ format: ' {bar} | {percentage}% | {value}/{total} | {task}',
177
+ }, cli_progress_1.default.Presets.shades_grey);
178
+ const renderBar = multibar.create(config.durationInFrames, 0, { task: 'Rendering' });
163
179
  const { audioAssets } = await (0, renderer_1.renderFrames)({
164
180
  url: options.url,
165
181
  config,
166
182
  outputDir: tmpDir,
167
183
  compositionId: selectedComp.id,
168
184
  inputProps,
169
- concurrency: options.concurrency || 1
185
+ concurrency: options.concurrency || 1,
186
+ onProgress: (frame) => renderBar.update(frame)
170
187
  });
171
- // Handle first audio asset for now (simplified)
172
- const audioFile = audioAssets.length > 0 ? audioAssets[0].src : undefined;
188
+ renderBar.update(config.durationInFrames);
189
+ // Resolve audio paths to absolute file paths if they are relative URLs
190
+ console.log(`DEBUG: Found ${audioAssets.length} audio assets in browser.`);
191
+ const resolvedAudioAssets = audioAssets.map(asset => {
192
+ console.log(`DEBUG: Processing asset: ${asset.src}`);
193
+ if (asset.src.startsWith('/') && !asset.src.startsWith('//')) {
194
+ // Look for public folder relative to the URL if possible, but for now
195
+ // let's try common locations
196
+ const possiblePaths = [
197
+ path_1.default.join(process.cwd(), 'examples/demo/public', asset.src.substring(1)),
198
+ path_1.default.join(process.cwd(), 'public', asset.src.substring(1)),
199
+ ];
200
+ for (const p of possiblePaths) {
201
+ console.log(`DEBUG: Checking path: ${p}`);
202
+ if (fs_1.default.existsSync(p)) {
203
+ console.log(`DEBUG: Found local file at ${p}!`);
204
+ return { ...asset, src: p };
205
+ }
206
+ }
207
+ }
208
+ return asset;
209
+ });
210
+ const encodeBar = multibar.create(100, 0, { task: 'Encoding ' });
173
211
  await (0, encoder_1.encodeVideo)({
174
212
  framesDir: tmpDir,
175
213
  fps: config.fps,
176
214
  outputFile: options.out,
177
- audioFile
215
+ audioAssets: resolvedAudioAssets,
216
+ onProgress: (percent) => encodeBar.update(Math.round(percent))
178
217
  });
179
- console.log(`Success! Video rendered to ${options.out}`);
218
+ encodeBar.update(100);
219
+ multibar.stop();
220
+ const endTime = Date.now();
221
+ const durationSec = ((endTime - startTime) / 1000).toFixed(1);
222
+ console.log(`\nSuccess! Video rendered to ${options.out}`);
223
+ console.log(`Total time: ${durationSec}s`);
180
224
  };
181
225
  exports.runRender = runRender;
182
226
  const main = () => {
@@ -211,7 +255,17 @@ const main = () => {
211
255
  .option('--duration <number>', 'Override duration in frames', parseInt)
212
256
  .action(async (options) => {
213
257
  try {
214
- await (0, exports.runRender)(options);
258
+ await (0, exports.runRender)({
259
+ url: options.url,
260
+ out: options.out,
261
+ compositionId: options.composition,
262
+ props: options.props,
263
+ concurrency: options.concurrency,
264
+ width: options.width,
265
+ height: options.height,
266
+ fps: options.fps,
267
+ duration: options.duration
268
+ });
215
269
  }
216
270
  catch (err) {
217
271
  console.error('Render failed:', err);
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@open-motion/cli",
3
- "version": "0.0.1-alpha.0",
3
+ "version": "0.0.2",
4
4
  "bin": {
5
5
  "open-motion": "dist/bin.js"
6
6
  },
7
7
  "license": "MIT",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/open-motion/open-motion.git",
10
+ "url": "https://github.com/jsongo/open-motion.git",
11
11
  "directory": "packages/cli"
12
12
  },
13
13
  "publishConfig": {
@@ -17,10 +17,14 @@
17
17
  "dist"
18
18
  ],
19
19
  "dependencies": {
20
+ "cli-progress": "^3.12.0",
20
21
  "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"
22
+ "@open-motion/encoder": "0.0.2",
23
+ "@open-motion/renderer": "0.0.2",
24
+ "@open-motion/core": "0.0.2"
25
+ },
26
+ "devDependencies": {
27
+ "@types/cli-progress": "^3.11.6"
24
28
  },
25
29
  "scripts": {
26
30
  "build": "tsc && chmod +x dist/bin.js"