vueseq 0.1.2 → 0.2.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/README.md CHANGED
@@ -15,48 +15,97 @@ Render Vue components with GSAP animations to video. Write standard Vue + GSAP c
15
15
  - ✅ **Simple CLI** - One command to render your video
16
16
  - ✅ **Programmatic API** - Integrate into your build pipeline
17
17
  - ✅ **Full GSAP Power** - All easing, timelines, and plugins work
18
+ - ✅ **Parallel Rendering** - Distributed capture across CPU cores (20x faster)
19
+ - ✅ **In-Browser Capture** - Zero-copy absolute performance
20
+ - ✅ **Auto-Scaling** - Automatically utilizes all available CPU cores
21
+ - ✅ **WebCodecs Encoding** - Hardware-accelerated H.264 encoding, no FFmpeg required
18
22
 
19
23
  ## Demo
20
24
 
21
-
22
-
23
25
  https://github.com/user-attachments/assets/84d01c02-4b4f-4d86-879e-720a7e367967
24
26
 
25
-
26
-
27
- *This video was rendered with VueSeq from [examples/HelloWorld.vue](./examples/HelloWorld.vue)*
27
+ _This video was rendered with VueSeq from [examples/Showcase.vue](./examples/Showcase.vue)_
28
28
 
29
29
  ## What Can You Build?
30
30
 
31
31
  **Your app. As a video. Using your actual components.**
32
32
 
33
- Install VueSeq into your Vue project and create stunning videos that perfectly match your brand—because they *are* your brand. No recreating UIs in After Effects. No screen recording artifacts. Just your components, animated with GSAP, rendered to pixel-perfect video.
33
+ Install VueSeq into your Vue project and create stunning videos that perfectly match your brand—because they _are_ your brand. No recreating UIs in After Effects. No screen recording artifacts. Just your components, animated with GSAP, rendered to pixel-perfect video.
34
34
 
35
35
  ### 🎬 App Showcases & Demos
36
+
36
37
  Create promotional videos using your real UI components. Product tours, feature walkthroughs, "What's New in v2.0" announcements—all with your exact design system, your exact colors, your exact components.
37
38
 
38
39
  ### 📱 Social Media Content
40
+
39
41
  Generate short-form videos for Twitter, LinkedIn, Instagram. Show off a feature in 15 seconds. Your followers see your actual product, not a mockup.
40
42
 
41
- ### 📚 Documentation & Tutorials
43
+ ### 📚 Documentation & Tutorials
44
+
42
45
  Animate how your features work. Show state transitions, user flows, component interactions. Embed videos in your docs that never go stale—regenerate them when your UI changes.
43
46
 
44
47
  ### 📊 Data Visualizations
48
+
45
49
  Animated charts, dashboards, infographics. Watch your bar charts grow, your line graphs draw, your data come alive.
46
50
 
51
+ ### 🎓 Educational Videos
52
+
53
+ Create animated explainers, course content, and training materials. Complex concepts become clear with step-by-step animated infographics and interactive walkthroughs.
54
+
47
55
  ### 🎨 Design System Demos
48
- Showcase your component library in motion. Let designers and developers *see* how components animate, transition, and interact.
56
+
57
+ Showcase your component library in motion. Let designers and developers _see_ how components animate, transition, and interact.
49
58
 
50
59
  ---
51
60
 
52
61
  **The idea is simple:** If you can build it in Vue, you can render it to video. One command. Deterministic output. Every time.
53
62
 
63
+ ## Integration Modes
64
+
65
+ ### 🔌 Add to Your Existing App
66
+
67
+ Install VueSeq into your Vue project and create videos using your existing components and design system. Import your buttons, cards, charts—anything you've already built. Your videos will match your app perfectly because they _are_ your app.
68
+
69
+ ```bash
70
+ npm install vueseq
71
+ # Create a video component that imports your existing components
72
+ npx vueseq src/videos/ProductDemo.vue -o demo.mp4
73
+ ```
74
+
75
+ ### 🤖 Programmatic / AI-Generated Videos
76
+
77
+ Use the API to generate videos programmatically. Perfect for:
78
+
79
+ - **AI pipelines**: Generate videos from LLM-created storyboards
80
+ - **Automated content**: Create personalized videos at scale
81
+ - **CI/CD integration**: Regenerate demo videos on every release
82
+
83
+ ```javascript
84
+ import { renderToMp4 } from 'vueseq'
85
+
86
+ await renderToMp4({
87
+ input: '/path/to/GeneratedVideo.vue',
88
+ output: 'output.mp4',
89
+ })
90
+ ```
91
+
92
+ ### 📦 Standalone Projects
93
+
94
+ Create a dedicated video project from scratch. Ideal for marketing teams, content creators, or anyone who wants to produce videos without an existing Vue app.
95
+
96
+ ```bash
97
+ mkdir my-video && cd my-video
98
+ npm init -y && npm install vueseq
99
+ # Create your video component and render
100
+ npx vueseq Video.vue -o video.mp4
101
+ ```
54
102
 
55
103
  ## Philosophy
56
104
 
57
- VueSeq is intentionally minimal. We bundle only the essentials: **Vue**, **GSAP**, **Playwright**, and **Vite**.
105
+ VueSeq is intentionally minimal. We bundle only the essentials: **Vue**, **GSAP**, **Playwright**, **Vite**, and **Mediabunny** (for WebCodecs-based encoding).
58
106
 
59
107
  We don't include CSS frameworks (Tailwind, UnoCSS, etc.) because:
108
+
60
109
  - Every developer has their preferred styling approach
61
110
  - Your project likely already has styling configured
62
111
  - Video components are self-contained—vanilla CSS works great
@@ -73,10 +122,7 @@ npm install vueseq
73
122
  ### Requirements
74
123
 
75
124
  - Node.js 18+
76
- - FFmpeg (for video encoding)
77
- - macOS: `brew install ffmpeg`
78
- - Ubuntu/Debian: `sudo apt install ffmpeg`
79
- - Windows: Download from [ffmpeg.org](https://ffmpeg.org/download.html)
125
+ - A modern Chromium browser (Playwright will download this automatically)
80
126
 
81
127
  ## Quick Start
82
128
 
@@ -95,25 +141,29 @@ const textRef = ref(null)
95
141
 
96
142
  onMounted(() => {
97
143
  const tl = gsap.timeline()
98
-
99
- tl.from(boxRef.value, {
100
- x: -200,
101
- opacity: 0,
144
+
145
+ tl.from(boxRef.value, {
146
+ x: -200,
147
+ opacity: 0,
102
148
  duration: 1,
103
- ease: 'power2.out'
149
+ ease: 'power2.out',
104
150
  })
105
-
106
- tl.from(textRef.value, {
107
- y: 50,
108
- opacity: 0,
109
- duration: 0.8,
110
- ease: 'back.out'
111
- }, '-=0.3')
112
-
151
+
152
+ tl.from(
153
+ textRef.value,
154
+ {
155
+ y: 50,
156
+ opacity: 0,
157
+ duration: 0.8,
158
+ ease: 'back.out',
159
+ },
160
+ '-=0.3',
161
+ )
162
+
113
163
  tl.to(boxRef.value, {
114
164
  rotation: 360,
115
165
  duration: 2,
116
- ease: 'elastic.out(1, 0.3)'
166
+ ease: 'elastic.out(1, 0.3)',
117
167
  })
118
168
  })
119
169
  </script>
@@ -162,6 +212,15 @@ span {
162
212
  npx vueseq examples/HelloWorld.vue -o examples/hello.mp4
163
213
  ```
164
214
 
215
+ ### 3. Parallel Rendering (Recommended for Speed)
216
+
217
+ For complex animations or long videos, use parallel rendering to utilize all CPU cores:
218
+
219
+ ```bash
220
+ npx vueseq examples/Showcase.vue --parallel
221
+ ```
222
+ *Automatically detects CPU cores and scales accordingly (e.g., 60s for a 20min render).*
223
+
165
224
  That's it! Duration is auto-detected from your timeline. Your video will be rendered at 1920x1080, 30fps.
166
225
 
167
226
  ## CLI Options
@@ -175,6 +234,12 @@ Options:
175
234
  -f, --fps Frames per second (default: 30)
176
235
  -w, --width Video width in pixels (default: 1920)
177
236
  -H, --height Video height in pixels (default: 1080)
237
+ --parallel Use parallel rendering (multi-process) [Recommended]
238
+ --workers Number of workers (default: auto-detected, use with --parallel)
239
+ --optimized Use optimized single-page in-browser capture
240
+ --gpu-backend Force GPU backend: vulkan, metal, d3d11, software (default: auto)
241
+ --monitor-memory Log memory usage
242
+ -v, --version Show version number
178
243
  --help Show help message
179
244
  ```
180
245
 
@@ -193,41 +258,39 @@ npx vueseq examples/Showcase.vue -d 10 -o examples/partial.mp4
193
258
  # 4K at 60fps
194
259
  npx vueseq examples/HelloWorld.vue -f 60 -w 3840 -H 2160 -o examples/hello-4k.mp4
195
260
 
196
- # Square for social media
197
- npx vueseq examples/HelloWorld.vue -w 1080 -H 1080 -o examples/hello-square.mp4
261
+ # Max Performance (Parallel + Auto-Scaling)
262
+ npx vueseq examples/Showcase.vue --parallel -o examples/showcase.mp4
263
+
264
+ # Dedicated Optimized Single-Page (Low Specs)
265
+ npx vueseq examples/Showcase.vue --optimized -o examples/showcase.mp4
266
+
267
+ # Force Vulkan Backend
268
+ npx vueseq examples/Showcase.vue --gpu-backend vulkan --parallel
269
+
270
+ # 4K at 60fps
271
+ npx vueseq examples/HelloWorld.vue -f 60 -w 3840 -H 2160 -o examples/hello-4k.mp4
198
272
  ```
199
273
 
200
274
  ## Programmatic API
201
275
 
202
276
  ```javascript
203
- import { renderToMp4, renderFrames, encodeVideo } from 'vueseq'
277
+ import { renderToMp4, isWebCodecsSupported } from 'vueseq'
278
+
279
+ // Check if WebCodecs is supported
280
+ const supported = await isWebCodecsSupported()
204
281
 
205
- // Render directly to MP4
282
+ // Render directly to MP4 (WebCodecs encoding)
206
283
  await renderToMp4({
207
284
  input: '/path/to/MyVideo.vue',
208
- duration: 5,
285
+ duration: 5, // Optional: auto-detected from timeline if not provided
209
286
  fps: 30,
210
287
  width: 1920,
211
288
  height: 1080,
212
289
  output: './output.mp4',
213
290
  onProgress: ({ frame, total, percent }) => {
214
291
  console.log(`Rendering: ${percent}%`)
215
- }
216
- })
217
-
218
- // Or render frames separately for custom processing
219
- const { framesDir, totalFrames, cleanup } = await renderFrames({
220
- input: '/path/to/MyVideo.vue',
221
- duration: 5,
222
- fps: 30,
223
- width: 1920,
224
- height: 1080
292
+ },
225
293
  })
226
-
227
- // Process frames here...
228
-
229
- await encodeVideo({ framesDir, output: './output.mp4', fps: 30 })
230
- await cleanup()
231
294
  ```
232
295
 
233
296
  ## How It Works
@@ -238,9 +301,9 @@ VueSeq uses GSAP's deterministic timeline control:
238
301
  2. **Playwright** opens it in headless Chrome
239
302
  3. For each frame, GSAP's `globalTimeline.seek(time)` jumps to the exact moment
240
303
  4. **Screenshot** captures the frame
241
- 5. **FFmpeg** encodes all frames to video
304
+ 5. **WebCodecs API** (via Mediabunny) encodes frames to video with hardware acceleration
242
305
 
243
- This is deterministic because `seek()` applies all GSAP values synchronously—given the same time, you get the exact same DOM state every time.
306
+ This is deterministic because `seek()` applies all GSAP values synchronously—given the same time, you get the exact same DOM state every time. The WebCodecs API provides hardware-accelerated H.264 encoding without requiring FFmpeg.
244
307
 
245
308
  ## Multi-Scene Videos
246
309
 
@@ -249,16 +312,16 @@ For longer videos with multiple scenes, use nested GSAP timelines:
249
312
  ```javascript
250
313
  onMounted(() => {
251
314
  const master = gsap.timeline()
252
-
315
+
253
316
  master.add(createIntro())
254
- master.add(createMainContent(), '-=0.5') // Overlap for smooth transition
317
+ master.add(createMainContent(), '-=0.5') // Overlap for smooth transition
255
318
  master.add(createOutro())
256
319
  })
257
320
 
258
321
  function createIntro() {
259
322
  const tl = gsap.timeline()
260
323
  tl.from('.title', { opacity: 0, duration: 1 })
261
- tl.to({}, { duration: 2 }) // Hold
324
+ tl.to({}, { duration: 2 }) // Hold
262
325
  tl.to('.scene-intro', { opacity: 0, duration: 0.5 })
263
326
  return tl
264
327
  }
package/bin/cli.js CHANGED
@@ -2,78 +2,109 @@
2
2
 
3
3
  /**
4
4
  * VueSeq CLI
5
- *
5
+ *
6
6
  * Render Vue + GSAP components to video.
7
- *
7
+ *
8
8
  * Usage:
9
9
  * vueseq <Video.vue> [options]
10
- *
10
+ *
11
11
  * Example:
12
12
  * vueseq MyAnimation.vue -d 5 -o my-video.mp4
13
13
  */
14
14
 
15
15
  import { parseArgs } from 'node:util'
16
16
  import { resolve, extname } from 'node:path'
17
- import { existsSync } from 'node:fs'
17
+ import { existsSync, readFileSync } from 'node:fs'
18
18
 
19
19
  // Show help text
20
20
  function showHelp() {
21
- console.log(`
21
+ console.log(`
22
22
  VueSeq - Render Vue + GSAP components to video
23
23
 
24
24
  USAGE:
25
25
  vueseq <Video.vue> [options]
26
26
 
27
27
  OPTIONS:
28
- -o, --output Output file (default: ./output.mp4)
29
- -d, --duration Duration in seconds (auto-detected if not specified)
30
- -f, --fps Frames per second (default: 30)
31
- -w, --width Video width in pixels (default: 1920)
32
- -H, --height Video height in pixels (default: 1080)
33
- --help Show this help message
28
+ -o, --output Output file (default: ./output.mp4)
29
+ -d, --duration Duration in seconds (auto-detected if not specified)
30
+ -f, --fps Frames per second (default: 30)
31
+ -w, --width Video width in pixels (default: 1920)
32
+ -H, --height Video height in pixels (default: 1080)
33
+ --gpu-backend GPU backend: auto, vulkan, egl, metal, d3d11, software (default: auto)
34
+ --optimized Use optimized in-browser capture (eliminates PNG overhead)
35
+ --parallel Use parallel frame capture with multiple browser pages
36
+ --workers Number of parallel workers (default: auto-detected cores)
37
+ --monitor-memory Log memory usage during rendering
38
+ --benchmark Compare original vs optimized render methods
39
+ -v, --version Show version number
40
+ --help Show this help message
34
41
 
35
42
  EXAMPLE:
36
43
  npx vueseq examples/HelloWorld.vue -o examples/hello.mp4
37
- npx vueseq examples/Showcase.vue -w 1280 -H 720 -f 60 -o examples/showcase.mp4
44
+ npx vueseq examples/HelloWorld.vue --optimized -o examples/hello.mp4
45
+ npx vueseq examples/Showcase.vue --parallel --workers 4 --monitor-memory
46
+
47
+ GPU DIAGNOSTICS:
48
+ node test-gpu.js # Run full GPU diagnostics
49
+ node test-gpu.js --benchmark # Include render benchmarks
38
50
  `)
39
51
  }
40
52
 
41
53
  // Parse command line arguments
42
54
  const { values, positionals } = parseArgs({
43
- allowPositionals: true,
44
- options: {
45
- output: { type: 'string', short: 'o', default: './output.mp4' },
46
- duration: { type: 'string', short: 'd' },
47
- fps: { type: 'string', short: 'f', default: '30' },
48
- width: { type: 'string', short: 'w', default: '1920' },
49
- height: { type: 'string', short: 'H', default: '1080' },
50
- help: { type: 'boolean' }
51
- }
55
+ allowPositionals: true,
56
+ options: {
57
+ output: { type: 'string', short: 'o', default: './output.mp4' },
58
+ duration: { type: 'string', short: 'd' },
59
+ fps: { type: 'string', short: 'f', default: '30' },
60
+ width: { type: 'string', short: 'w', default: '1920' },
61
+ height: { type: 'string', short: 'H', default: '1080' },
62
+ 'gpu-backend': { type: 'string', default: 'auto' },
63
+ optimized: { type: 'boolean', default: false },
64
+ parallel: { type: 'boolean', default: false },
65
+ workers: { type: 'string' },
66
+ 'monitor-memory': { type: 'boolean', default: false },
67
+ benchmark: { type: 'boolean', default: false },
68
+ version: { type: 'boolean', short: 'v' },
69
+ help: { type: 'boolean' },
70
+ },
52
71
  })
53
72
 
73
+ // Show version if requested
74
+ if (values.version) {
75
+ try {
76
+ const pkgPath = new URL('../package.json', import.meta.url)
77
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
78
+ console.log(`vueseq v${pkg.version}`)
79
+ } catch (e) {
80
+ console.error('Error reading version:', e.message)
81
+ }
82
+ process.exit(0)
83
+ }
84
+
54
85
  // Show help if requested
55
86
  if (values.help) {
56
- showHelp()
57
- process.exit(0)
87
+ showHelp()
88
+ process.exit(0)
58
89
  }
59
90
 
60
91
  // Validate input file
61
92
  const input = positionals[0]
62
93
  if (!input) {
63
- console.error('Error: Please specify a .vue file\n')
64
- showHelp()
65
- process.exit(1)
94
+ console.error('Error: Please specify a .vue file\n')
95
+ showHelp()
96
+ process.exit(1)
66
97
  }
67
98
 
68
99
  const inputPath = resolve(input)
69
100
  if (!existsSync(inputPath)) {
70
- console.error(`Error: File not found: ${inputPath}`)
71
- process.exit(1)
101
+ console.error(`Error: File not found: ${inputPath}`)
102
+ process.exit(1)
72
103
  }
73
104
 
74
105
  if (extname(inputPath) !== '.vue') {
75
- console.error('Error: Input must be a .vue file')
76
- process.exit(1)
106
+ console.error('Error: Input must be a .vue file')
107
+ process.exit(1)
77
108
  }
78
109
 
79
110
  // Parse duration if provided
@@ -81,13 +112,13 @@ let duration = null
81
112
  let durationAuto = false
82
113
 
83
114
  if (values.duration) {
84
- duration = parseFloat(values.duration)
85
- if (isNaN(duration) || duration <= 0) {
86
- console.error('Error: Duration must be a positive number')
87
- process.exit(1)
88
- }
115
+ duration = parseFloat(values.duration)
116
+ if (isNaN(duration) || duration <= 0) {
117
+ console.error('Error: Duration must be a positive number')
118
+ process.exit(1)
119
+ }
89
120
  } else {
90
- durationAuto = true
121
+ durationAuto = true
91
122
  }
92
123
 
93
124
  // Parse numeric options
@@ -96,59 +127,134 @@ const width = parseInt(values.width)
96
127
  const height = parseInt(values.height)
97
128
 
98
129
  if (isNaN(fps) || fps <= 0) {
99
- console.error('Error: FPS must be a positive number')
100
- process.exit(1)
130
+ console.error('Error: FPS must be a positive number')
131
+ process.exit(1)
101
132
  }
102
133
 
103
134
  if (isNaN(width) || width <= 0 || isNaN(height) || height <= 0) {
104
- console.error('Error: Width and height must be positive numbers')
105
- process.exit(1)
135
+ console.error('Error: Width and height must be positive numbers')
136
+ process.exit(1)
106
137
  }
107
138
 
108
139
  // Import renderer and start rendering
109
140
  try {
110
- const { renderToMp4 } = await import('../src/renderer/encode.js')
111
- const { getTimelineDuration } = await import('../src/renderer/render.js')
112
-
113
- // Auto-detect duration if not provided
114
- if (durationAuto) {
115
- console.log(`\nVueSeq - Detecting timeline duration...`)
116
- duration = await getTimelineDuration({ input: inputPath, width, height })
117
- if (!duration || duration <= 0) {
118
- console.error('Error: Could not auto-detect duration. Use -d to specify manually.')
119
- process.exit(1)
120
- }
121
- console.log(` Auto-detected: ${duration.toFixed(2)}s`)
122
- }
141
+ const { renderToMp4, renderToMp4Optimized, benchmarkRenderMethods } =
142
+ await import('../src/renderer/encode.js')
143
+ const { renderToMp4Parallel } = await import(
144
+ '../src/renderer/encode-parallel.js'
145
+ )
146
+ const { getTimelineDuration } = await import('../src/renderer/render.js')
147
+ const { detectBestGPUConfig, clearGPUCache } = await import(
148
+ '../src/renderer/gpu.js'
149
+ )
150
+
151
+ // Get GPU backend preference
152
+ const gpuBackend = values['gpu-backend'] || 'auto'
153
+
154
+ // Clear cache if specific backend requested (force re-detection)
155
+ if (gpuBackend !== 'auto') {
156
+ await clearGPUCache()
157
+ }
123
158
 
124
- console.log(`\nVueSeq - Rendering ${input}`)
125
- console.log(` Duration: ${duration}s at ${fps}fps (${Math.ceil(duration * fps)} frames)${durationAuto ? ' (auto)' : ''}`)
126
- console.log(` Resolution: ${width}x${height}`)
127
- console.log(` Output: ${values.output}\n`)
128
-
129
- const startTime = Date.now()
130
- let lastLoggedPercent = -1
131
-
132
- await renderToMp4({
133
- input: inputPath,
134
- duration,
135
- fps,
136
- width,
137
- height,
138
- output: values.output,
139
- onProgress: ({ frame, total, percent }) => {
140
- // Only log every 5% to reduce noise
141
- if (percent % 5 === 0 && percent !== lastLoggedPercent) {
142
- lastLoggedPercent = percent
143
- process.stdout.write(`\rRendering: ${percent}% (${frame + 1}/${total} frames)`)
144
- }
145
- }
159
+ // Check GPU acceleration status
160
+ const gpuConfig = await detectBestGPUConfig({
161
+ preferBackend: gpuBackend !== 'auto' ? gpuBackend : undefined,
162
+ })
163
+
164
+ const gpuIcon = gpuConfig.isHardwareAccelerated ? '✓' : '○'
165
+ const gpuMode = gpuConfig.isHardwareAccelerated ? 'Hardware' : 'Software'
166
+ console.log(`\nGPU: ${gpuIcon} ${gpuMode}(${gpuConfig.label})`)
167
+ console.log(` Renderer: ${gpuConfig.renderer}`)
168
+
169
+ // Run benchmark mode if requested
170
+ if (values.benchmark) {
171
+ console.log('\n📊 Running render benchmark...')
172
+ await benchmarkRenderMethods({
173
+ input: inputPath,
174
+ duration: duration || 2,
175
+ fps,
176
+ width,
177
+ height,
146
178
  })
179
+ process.exit(0)
180
+ }
181
+
182
+ // Auto-detect duration if not provided
183
+ if (durationAuto) {
184
+ console.log(`\nVueSeq - Detecting timeline duration...`)
185
+ duration = await getTimelineDuration({ input: inputPath, width, height })
186
+ if (!duration || duration <= 0) {
187
+ console.error(
188
+ 'Error: Could not auto-detect duration. Use -d to specify manually.',
189
+ )
190
+ process.exit(1)
191
+ }
192
+ console.log(` Auto - detected: ${duration.toFixed(2)}s`)
193
+ }
194
+
195
+ // Select renderer based on flags
196
+ const useParallel = values.parallel
197
+ const useOptimized = values.optimized
198
+ const numWorkers = values.workers ? parseInt(values.workers, 10) : undefined
199
+ const monitorMemory = values['monitor-memory']
200
+
201
+ let renderMethod
202
+ if (useParallel) {
203
+ renderMethod = `Parallel(${numWorkers || 'Auto'} workers)`
204
+ } else if (useOptimized) {
205
+ renderMethod = 'Optimized (in-browser capture)'
206
+ } else {
207
+ renderMethod = 'Standard (PNG-based)'
208
+ }
147
209
 
148
- const elapsed = ((Date.now() - startTime) / 1000).toFixed(1)
149
- console.log(`\n\n✓ Video saved to ${values.output} (${elapsed}s)`)
210
+ console.log(`\nVueSeq - Rendering ${input} `)
211
+ console.log(` Method: ${renderMethod} `)
212
+ console.log(
213
+ ` Duration: ${duration}s at ${fps} fps(${Math.ceil(duration * fps)} frames)${durationAuto ? ' (auto)' : ''} `,
214
+ )
215
+ console.log(` Resolution: ${width}x${height} `)
216
+ console.log(` Output: ${values.output} \n`)
150
217
 
218
+ const startTime = Date.now()
219
+ let lastLoggedPercent = -1
220
+
221
+ // Select render function
222
+ let renderFn
223
+ if (useParallel) {
224
+ renderFn = renderToMp4Parallel
225
+ } else if (useOptimized) {
226
+ renderFn = renderToMp4Optimized
227
+ } else {
228
+ renderFn = renderToMp4
229
+ }
230
+
231
+ await renderFn({
232
+ input: inputPath,
233
+ duration,
234
+ fps,
235
+ width,
236
+ height,
237
+ output: values.output,
238
+ workers: numWorkers,
239
+ monitorMemory,
240
+ onProgress: ({ frame, total, percent, workerId }) => {
241
+ // Only log every 5% to reduce noise
242
+ if (percent % 5 === 0 && percent !== lastLoggedPercent) {
243
+ lastLoggedPercent = percent
244
+ const workerInfo = workerId !== undefined ? ` [W${workerId}]` : ''
245
+ process.stdout.write(
246
+ `\rRendering: ${percent}% (${frame + 1}/${total} frames)${workerInfo} `,
247
+ )
248
+ }
249
+ },
250
+ })
251
+
252
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1)
253
+ console.log(`\n\n✓ Video saved to ${values.output} (${elapsed}s)`)
151
254
  } catch (error) {
152
- console.error(`\nError: ${error.message}`)
153
- process.exit(1)
255
+ console.error(`\nError: ${error.message} `)
256
+ process.exit(1)
154
257
  }
258
+
259
+
260
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueseq",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "A minimal, deterministic video renderer for Vue 3 + GSAP",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,10 +38,15 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "gsap": "^3.12.0",
41
- "playwright": "^1.40.0"
41
+ "html2canvas": "^1.4.1",
42
+ "mediabunny": "^1.31.0",
43
+ "playwright": "^1.58.0"
42
44
  },
43
45
  "devDependencies": {
46
+ "@semantic-release/changelog": "^6.0.3",
47
+ "@semantic-release/git": "^10.0.1",
44
48
  "@vitejs/plugin-vue": "^5.0.0",
49
+ "semantic-release": "^25.0.2",
45
50
  "vite": "^5.0.0",
46
51
  "vue": "^3.4.0"
47
52
  },
package/src/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * VueSeq - Public API
3
- *
3
+ *
4
4
  * A minimal, deterministic video renderer for Vue 3 + GSAP.
5
+ * Uses WebCodecs API for hardware-accelerated video encoding.
5
6
  */
6
7
 
7
8
  export { renderFrames } from './renderer/render.js'
8
- export { encodeVideo, renderToMp4 } from './renderer/encode.js'
9
+ export { renderToMp4, isWebCodecsSupported } from './renderer/encode.js'
9
10
  export { createVideoServer } from './bundler/vite.js'