(templateId: string, projectDir: string)
| 136 | } |
| 137 | |
| 138 | async function generateThumbnail(templateId: string, projectDir: string): Promise<void> { |
| 139 | const config = TEMPLATE_CONFIG[templateId] ?? DEFAULT_CONFIG; |
| 140 | |
| 141 | const framesDir = join(projectDir, "_thumb_frames"); |
| 142 | mkdirSync(framesDir, { recursive: true }); |
| 143 | |
| 144 | const fileServer = await createFileServer({ |
| 145 | projectDir, |
| 146 | port: 0, |
| 147 | fps: { num: 30, den: 1 }, |
| 148 | }); |
| 149 | try { |
| 150 | const session = await createCaptureSession(fileServer.url, framesDir, { |
| 151 | width: config.width, |
| 152 | height: config.height, |
| 153 | fps: 30, |
| 154 | format: "png", |
| 155 | }); |
| 156 | await initializeSession(session); |
| 157 | |
| 158 | let duration: number; |
| 159 | try { |
| 160 | duration = await getCompositionDuration(session); |
| 161 | } catch { |
| 162 | duration = 5; |
| 163 | } |
| 164 | |
| 165 | const t = Math.min(config.captureTime, duration * 0.8); |
| 166 | const result = await captureFrame(session, 0, t); |
| 167 | cpSync(result.path, join(outputDir, `${templateId}.png`)); |
| 168 | console.log(` ✓ ${templateId}.png (${result.captureTimeMs}ms)`); |
| 169 | |
| 170 | await closeCaptureSession(session); |
| 171 | } finally { |
| 172 | fileServer.close(); |
| 173 | rmSync(framesDir, { recursive: true, force: true }); |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | async function generateVideo(templateId: string, projectDir: string): Promise<void> { |
| 178 | const outMp4 = join(outputDir, `${templateId}.mp4`); |
no test coverage detected