import { expect, test } from "@playwright/test"; import fs from "node:fs"; import path from "node:path"; const root = path.resolve(import.meta.dirname, "../../.."); const blend = fs.readFileSync(path.join(root, "tests/files/web/sequencer_scene.blend")); const golden = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/M11-12/sequencer-final-export.json"), "utf8")) as { sourceBlendSha256: string; settingsSha256: string; requestSha256: string; }; test("M11-12 routes a real Main timeline to server export even when VideoEncoder exists", async ({ page }) => { await page.goto("/"); const result = await page.evaluate(async (input) => { const [{ WebEngineClient }, finalExport] = await Promise.all([ import("/src/engine-client/WebEngineClient.ts"), import("/src/sequencer/SequencerFinalExport.ts"), ]); const source = Uint8Array.from(input).buffer; const sourceBlendSha256 = Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", source)), (byte) => byte.toString(16).padStart(2, "0")).join(""); const client = new WebEngineClient({ timeoutMs: 20_000 }); try { const opened = await client.openBlend(source); const timeline = opened.snapshot.scenes.find((scene) => scene.name === "SequencerScene")?.sequencerTimeline; if (!timeline) throw new Error("Real Sequencer Main timeline is missing"); const request = { schemaVersion: 1 as const, timelineId: timeline.id, timelineRevision: timeline.revision, sourceBlendSha256, frameStart: timeline.frameStart, frameEnd: timeline.frameEnd, fpsNumerator: timeline.fpsNumerator, fpsDenominator: timeline.fpsDenominator, width: 1920, height: 1080, container: "MPEG4" as const, videoCodec: "H264" as const, audioCodec: "AAC" as const, }; const withoutServer = await finalExport.routeSequencerFinalExportInBrowser(request, false, {}); const serverWithoutEncoder = await finalExport.routeSequencerFinalExportInBrowser(request, true, {}); const serverWithEncoder = await finalExport.routeSequencerFinalExportInBrowser( request, true, { VideoEncoder: class TestVideoEncoder {} }, ); const runtime = await finalExport.routeSequencerFinalExportInBrowser(request, true); return { timeline, sourceBlendSha256, withoutServer, serverWithoutEncoder, serverWithEncoder, runtime }; } finally { client.terminate(); } }, Array.from(blend)); expect(result.timeline).toMatchObject({ id: "sequencer:scene:SequencerScene", revision: 1, frameStart: 1, frameEnd: 250, fpsNumerator: 24000, fpsDenominator: 1001, }); expect(result.sourceBlendSha256).toBe(golden.sourceBlendSha256); expect(result.withoutServer).toMatchObject({ requestSha256: golden.requestSha256, settingsSha256: golden.settingsSha256, route: "SERVER_EXPORT", status: "BLOCKED", code: "SEQUENCER_EXPORT_SERVER_UNAVAILABLE", localEncoding: "BLOCKED", browserVideoEncoderDetected: false, }); expect(result.serverWithoutEncoder).toMatchObject({ requestSha256: golden.requestSha256, settingsSha256: golden.settingsSha256, route: "SERVER_EXPORT", status: "SERVER_EXPORT_REQUIRED", code: null, localEncoding: "BLOCKED", browserVideoEncoderDetected: false, }); expect(result.serverWithEncoder).toMatchObject({ ...result.serverWithoutEncoder, browserVideoEncoderDetected: true, }); expect(result.runtime).toMatchObject({ route: "SERVER_EXPORT", status: "SERVER_EXPORT_REQUIRED", localEncoding: "BLOCKED", }); });