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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Frame Renderer
3
- *
3
+ *
4
4
  * The core rendering loop that captures each frame using Playwright.
5
5
  * For each frame:
6
6
  * 1. Seek GSAP globalTimeline to the exact time
@@ -12,6 +12,10 @@ import { chromium } from 'playwright'
12
12
  import { createVideoServer } from '../bundler/vite.js'
13
13
  import { mkdir } from 'fs/promises'
14
14
  import { join } from 'path'
15
+ import { getOptimalChromiumConfig } from './gpu.js'
16
+
17
+ // GPU configuration is now handled by the gpu.js module
18
+ // which auto-detects the best backend for the current system
15
19
 
16
20
  /**
17
21
  * Get the timeline duration from a Vue component
@@ -22,32 +26,43 @@ import { join } from 'path'
22
26
  * @returns {Promise<number|null>} Duration in seconds, or null if not detectable
23
27
  */
24
28
  export async function getTimelineDuration(options) {
25
- const { input, width = 1920, height = 1080 } = options
26
-
27
- const { url, cleanup } = await createVideoServer({ input, width, height })
28
-
29
- const browser = await chromium.launch({ headless: true })
30
- const context = await browser.newContext({
31
- viewport: { width, height },
32
- deviceScaleFactor: 1
29
+ const { input, width = 1920, height = 1080 } = options
30
+
31
+ const { url, cleanup } = await createVideoServer({ input, width, height })
32
+
33
+ // Get optimal GPU configuration for this system
34
+ const gpuConfig = await getOptimalChromiumConfig()
35
+
36
+ const launchOptions = {
37
+ headless: gpuConfig.headless,
38
+ args: gpuConfig.args,
39
+ }
40
+ if (gpuConfig.channel) {
41
+ launchOptions.channel = gpuConfig.channel
42
+ }
43
+ const browser = await chromium.launch(launchOptions)
44
+ const context = await browser.newContext({
45
+ viewport: { width, height },
46
+ deviceScaleFactor: 1,
47
+ })
48
+ const page = await context.newPage()
49
+
50
+ try {
51
+ await page.goto(url, { waitUntil: 'networkidle' })
52
+ await page.waitForFunction(() => window.__VUESEQ_READY__ === true, {
53
+ timeout: 30000,
33
54
  })
34
- const page = await context.newPage()
35
-
36
- try {
37
- await page.goto(url, { waitUntil: 'networkidle' })
38
- await page.waitForFunction(
39
- () => window.__VUESEQ_READY__ === true,
40
- { timeout: 30000 }
41
- )
42
- // Give Vue/GSAP a moment to set up timelines
43
- await page.waitForTimeout(100)
44
-
45
- const duration = await page.evaluate(() => window.__VUESEQ_GET_DURATION__?.())
46
- return duration
47
- } finally {
48
- await browser.close()
49
- await cleanup()
50
- }
55
+ // Give Vue/GSAP a moment to set up timelines
56
+ await page.waitForTimeout(100)
57
+
58
+ const duration = await page.evaluate(() =>
59
+ window.__VUESEQ_GET_DURATION__?.(),
60
+ )
61
+ return duration
62
+ } finally {
63
+ await browser.close()
64
+ await cleanup()
65
+ }
51
66
  }
52
67
 
53
68
  /**
@@ -62,87 +77,101 @@ export async function getTimelineDuration(options) {
62
77
  * @returns {Promise<{framesDir: string, totalFrames: number, cleanup: () => Promise<void>}>}
63
78
  */
64
79
  export async function renderFrames(options) {
65
- let {
66
- input,
67
- fps = 30,
68
- duration,
69
- width = 1920,
70
- height = 1080,
71
- onProgress
72
- } = options
73
-
74
- // Auto-detect duration if not provided
75
- if (!duration) {
76
- duration = await getTimelineDuration({ input, width, height })
77
- if (!duration || duration <= 0) {
78
- throw new Error('Could not auto-detect duration. Specify -d/--duration manually.')
79
- }
80
+ let {
81
+ input,
82
+ fps = 30,
83
+ duration,
84
+ width = 1920,
85
+ height = 1080,
86
+ onProgress,
87
+ } = options
88
+
89
+ // Auto-detect duration if not provided
90
+ if (!duration) {
91
+ duration = await getTimelineDuration({ input, width, height })
92
+ if (!duration || duration <= 0) {
93
+ throw new Error(
94
+ 'Could not auto-detect duration. Specify -d/--duration manually.',
95
+ )
80
96
  }
81
-
82
- const totalFrames = Math.ceil(duration * fps)
83
-
84
- // Start Vite server
85
- const { url, tempDir, cleanup } = await createVideoServer({ input, width, height })
86
-
87
- const framesDir = join(tempDir, 'frames')
88
- await mkdir(framesDir, { recursive: true })
89
-
90
- // Launch headless browser
91
- const browser = await chromium.launch({
92
- headless: true
93
- })
94
-
95
- const context = await browser.newContext({
96
- viewport: { width, height },
97
- deviceScaleFactor: 1
97
+ }
98
+
99
+ const totalFrames = Math.ceil(duration * fps)
100
+
101
+ // Start Vite server
102
+ const { url, tempDir, cleanup } = await createVideoServer({
103
+ input,
104
+ width,
105
+ height,
106
+ })
107
+
108
+ const framesDir = join(tempDir, 'frames')
109
+ await mkdir(framesDir, { recursive: true })
110
+
111
+ // Launch headless browser with optimal GPU config
112
+ const gpuConfig = await getOptimalChromiumConfig()
113
+ const launchOptions2 = {
114
+ headless: gpuConfig.headless,
115
+ args: gpuConfig.args,
116
+ }
117
+ if (gpuConfig.channel) {
118
+ launchOptions2.channel = gpuConfig.channel
119
+ }
120
+ const browser = await chromium.launch(launchOptions2)
121
+
122
+ const context = await browser.newContext({
123
+ viewport: { width, height },
124
+ deviceScaleFactor: 1,
125
+ })
126
+
127
+ const page = await context.newPage()
128
+
129
+ try {
130
+ // Load the page
131
+ await page.goto(url, { waitUntil: 'networkidle' })
132
+
133
+ // Wait for VueSeq bridge to be ready
134
+ await page.waitForFunction(() => window.__VUESEQ_READY__ === true, {
135
+ timeout: 30000,
98
136
  })
99
137
 
100
- const page = await context.newPage()
101
-
102
- try {
103
- // Load the page
104
- await page.goto(url, { waitUntil: 'networkidle' })
105
-
106
- // Wait for VueSeq bridge to be ready
107
- await page.waitForFunction(
108
- () => window.__VUESEQ_READY__ === true,
109
- { timeout: 30000 }
110
- )
111
-
112
- // Give Vue a moment to mount and GSAP to set up timelines
113
- await page.waitForTimeout(100)
114
-
115
- // Render each frame
116
- for (let frame = 0; frame < totalFrames; frame++) {
117
- const timeInSeconds = frame / fps
118
-
119
- // Seek GSAP to exact time and wait for paint
120
- await page.evaluate(async (t) => {
121
- window.__VUESEQ_SEEK__(t)
122
- // Wait for next animation frame to ensure DOM is painted
123
- await new Promise(resolve => requestAnimationFrame(resolve))
124
- }, timeInSeconds)
125
-
126
- // Take screenshot
127
- const framePath = join(framesDir, `frame-${String(frame).padStart(5, '0')}.png`)
128
- await page.screenshot({
129
- path: framePath,
130
- type: 'png'
131
- })
132
-
133
- // Progress callback
134
- if (onProgress) {
135
- onProgress({
136
- frame,
137
- total: totalFrames,
138
- timeInSeconds,
139
- percent: Math.round((frame + 1) / totalFrames * 100)
140
- })
141
- }
142
- }
143
- } finally {
144
- await browser.close()
138
+ // Give Vue a moment to mount and GSAP to set up timelines
139
+ await page.waitForTimeout(100)
140
+
141
+ // Render each frame
142
+ for (let frame = 0; frame < totalFrames; frame++) {
143
+ const timeInSeconds = frame / fps
144
+
145
+ // Seek GSAP to exact time and wait for paint
146
+ await page.evaluate(async (t) => {
147
+ window.__VUESEQ_SEEK__(t)
148
+ // Wait for next animation frame to ensure DOM is painted
149
+ await new Promise((resolve) => requestAnimationFrame(resolve))
150
+ }, timeInSeconds)
151
+
152
+ // Take screenshot
153
+ const framePath = join(
154
+ framesDir,
155
+ `frame-${String(frame).padStart(5, '0')}.png`,
156
+ )
157
+ await page.screenshot({
158
+ path: framePath,
159
+ type: 'png',
160
+ })
161
+
162
+ // Progress callback
163
+ if (onProgress) {
164
+ onProgress({
165
+ frame,
166
+ total: totalFrames,
167
+ timeInSeconds,
168
+ percent: Math.round(((frame + 1) / totalFrames) * 100),
169
+ })
170
+ }
145
171
  }
172
+ } finally {
173
+ await browser.close()
174
+ }
146
175
 
147
- return { framesDir, totalFrames, cleanup }
176
+ return { framesDir, totalFrames, cleanup }
148
177
  }