From 20f1a167fabe94506c423ec977ff017a7066ffb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Mon, 30 Mar 2026 18:09:47 +0200 Subject: [PATCH] fix(cli): implement generateThumbnail in studio adapter The CLI adapter was missing generateThumbnail, so the thumbnail route always returned 501 and composition thumbnails showed as blank black rectangles in both the Compositions sidebar and the Timeline. --- packages/cli/src/server/studioServer.ts | 41 +++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/packages/cli/src/server/studioServer.ts b/packages/cli/src/server/studioServer.ts index 53296091d..4892a05a8 100644 --- a/packages/cli/src/server/studioServer.ts +++ b/packages/cli/src/server/studioServer.ts @@ -152,6 +152,47 @@ export function createStudioServer(options: StudioServerOptions): StudioServer { return state; }, + + async generateThumbnail(opts): Promise { + let instance: import("puppeteer-core").Browser | null = null; + try { + const { ensureBrowser } = await import("../browser/manager.js"); + const browser = await ensureBrowser(); + const puppeteer = await import("puppeteer-core"); + instance = await puppeteer.launch({ + executablePath: browser.executablePath, + headless: true, + args: ["--no-sandbox", "--disable-setuid-sandbox"], + }); + const page = await instance.newPage(); + await page.setViewport({ width: opts.width || 1920, height: opts.height || 1080 }); + await page.goto(opts.previewUrl, { waitUntil: "networkidle2", timeout: 15000 }); + await page + .waitForFunction(() => (window as any).__playerReady || (window as any).__timelines, { + timeout: 8000, + }) + .catch(() => {}); + await page.evaluate((t: number) => { + const win = window as any; + if (win.__player?.seek) win.__player.seek(t); + else if (win.__timeline?.seek) { + win.__timeline.pause(); + win.__timeline.seek(t); + } + }, opts.seekTime); + await page.waitForTimeout(300); + const screenshot = (await page.screenshot({ type: "jpeg", quality: 85 })) as Buffer; + return screenshot; + } catch { + return null; + } finally { + try { + await instance?.close(); + } catch { + /* ignore close errors */ + } + } + }, }; // ── Build the Hono app ─────────────────────────────────────────────────