fix(studio): preserve alpha proxy playback (#2625)

* fix(studio): preserve alpha proxy playback

* test(cli): pin VP8 alpha proxy pre-resolution

* fix(studio): emit textarea field-sizing CSS

* test(studio): verify textarea CSS generation
This commit is contained in:
Miguel Ángel
2026-07-17 21:56:27 -04:00
committed by GitHub
parent bcdb90a45f
commit e64a22893a
15 changed files with 125 additions and 88 deletions
+8 -3
View File
@@ -69,7 +69,7 @@ vi.mock("./staticProjectServer.js", () => ({
vi.mock("@hyperframes/studio-server/media-codec-map", async (importOriginal) => ({
...(await importOriginal<typeof import("@hyperframes/studio-server/media-codec-map")>()),
scanProjectMediaCodecMap: mocks.scanProjectMediaCodecMap,
proxyVariantFor: (facts: { hasAlpha?: boolean }) => (facts.hasAlpha ? "vp9" : "h264"),
proxyVariantFor: (facts: { hasAlpha?: boolean }) => (facts.hasAlpha ? "vp8" : "h264"),
}));
vi.mock("@hyperframes/studio-server/proxy-transcoder", () => ({
resolveProxy: mocks.resolveProxy,
@@ -502,7 +502,7 @@ describe("preResolveHostileMediaProxies", () => {
);
});
it("does not pre-resolve a hostile asset rejected by the shared proxy policy", async () => {
it("pre-resolves an alpha VP9 asset through the Chromium-compatible VP8 proxy", async () => {
const projectDir = mkProjectDir();
mocks.scanProjectMediaCodecMap.mockResolvedValue({
"/alpha.webm": {
@@ -515,7 +515,12 @@ describe("preResolveHostileMediaProxies", () => {
await preResolveHostileMediaProxies(projectDir, "<html></html>");
expect(mocks.resolveProxy).not.toHaveBeenCalled();
expect(mocks.resolveProxy).toHaveBeenCalledTimes(1);
expect(mocks.resolveProxy).toHaveBeenCalledWith(
projectDir,
join(projectDir, "alpha.webm"),
"vp8",
);
});
it("is a no-op when the codec map has no hostile entries", async () => {
@@ -49,7 +49,7 @@ vi.mock("@hyperframes/studio-server/proxy-transcoder", () => ({
vi.mock("@hyperframes/studio-server/media-codec-map", () => ({
scanProjectMediaCodecMap: mocks.scanProjectMediaCodecMap,
proxyVariantFor: (facts: { hasAlpha?: boolean }) => (facts.hasAlpha ? "vp9" : "h264"),
proxyVariantFor: (facts: { hasAlpha?: boolean }) => (facts.hasAlpha ? "vp8" : "h264"),
}));
const { bakeMediaProxies, PROXY_ARCHIVE_PREFIX } = await import("./publishProxyBake.js");
@@ -196,7 +196,7 @@ describe("bakeMediaProxies", () => {
expect(html).not.toContain("assets/my%20clip.mp4");
});
it("bakes an alpha-bearing hostile asset as a VP9 WebM proxy", async () => {
it("bakes an alpha-bearing hostile asset as a VP8 WebM proxy", async () => {
mocks.scanProjectMediaCodecMap.mockResolvedValue({
"/clip.mov": {
codecName: "prores",
@@ -205,7 +205,7 @@ describe("bakeMediaProxies", () => {
hasAlpha: true,
},
});
const proxyPath = tmpProxyFile("PROXY_VP9_ALPHA_BYTES", ".webm");
const proxyPath = tmpProxyFile("PROXY_VP8_ALPHA_BYTES", ".webm");
mocks.resolveProxy.mockResolvedValue(proxyPath);
const fileContents = new Map<string, Buffer>([
["index.html", indexHtml(`<video src="clip.mov" muted></video>`)],
@@ -217,13 +217,13 @@ describe("bakeMediaProxies", () => {
expect(mocks.resolveProxy).toHaveBeenCalledWith(
PROJECT_DIR,
join(PROJECT_DIR, "clip.mov"),
"vp9",
"vp8",
);
const proxyEntries = [...fileContents.keys()].filter((key) =>
key.startsWith(`${PROXY_ARCHIVE_PREFIX}/`),
);
expect(proxyEntries).toEqual([`${PROXY_ARCHIVE_PREFIX}/proxy.webm`]);
expect(fileContents.get(proxyEntries[0]!)?.toString("utf-8")).toBe("PROXY_VP9_ALPHA_BYTES");
expect(fileContents.get(proxyEntries[0]!)?.toString("utf-8")).toBe("PROXY_VP8_ALPHA_BYTES");
expect(fileContents.get("index.html")?.toString("utf-8")).toContain(proxyEntries[0]!);
expect(manifest).toEqual({ proxied: ["/clip.mov"], skippedAlpha: [], failed: [] });
});
+2 -2
View File
@@ -20,7 +20,7 @@
* `cloud render` never calls this: it builds and zips the file map without an
* intermediate baking transform (R2 in the plan).
*
* Alpha-bearing sources bake as VP9/WebM so transparency survives. A failed
* Alpha-bearing sources bake as VP8/WebM so transparency survives. A failed
* hostile transcode aborts publish with a
* structured manifest rather than silently shipping an unplayable asset.
*/
@@ -52,7 +52,7 @@ export const PROXY_ARCHIVE_PREFIX = "_proxy";
export interface ProxyBakeManifest {
proxied: string[];
/** @deprecated Alpha sources are proxied as VP9; retained as an always-empty compatibility field. */
/** @deprecated Alpha sources are proxied as VP8; retained as an always-empty compatibility field. */
skippedAlpha: string[];
failed: Array<{ path: string; error: string }>;
}
@@ -69,16 +69,16 @@ vi.mock("@hyperframes/studio-server/proxy-transcoder", () => ({
vi.mock("@hyperframes/studio-server/media-codec-map", () => ({
probeAssetCodec: mocks.probeAssetCodec,
decideMediaProxyEligibility: mocks.decideMediaProxyEligibility,
isProxyVariant: (value: string) => value === "h264" || value === "vp9",
isProxyVariantRequest: (value: string) => value === "auto" || value === "h264" || value === "vp9",
proxyVariantFor: (facts: { hasAlpha?: boolean }) => (facts.hasAlpha ? "vp9" : "h264"),
resolveProxyVariantRequest: (request: "auto" | "h264" | "vp9", facts: { hasAlpha?: boolean }) => {
const expected = facts.hasAlpha ? "vp9" : "h264";
isProxyVariant: (value: string) => value === "h264" || value === "vp8",
isProxyVariantRequest: (value: string) => value === "auto" || value === "h264" || value === "vp8",
proxyVariantFor: (facts: { hasAlpha?: boolean }) => (facts.hasAlpha ? "vp8" : "h264"),
resolveProxyVariantRequest: (request: "auto" | "h264" | "vp8", facts: { hasAlpha?: boolean }) => {
const expected = facts.hasAlpha ? "vp8" : "h264";
return request === "auto" || request === expected ? expected : null;
},
PROXY_VARIANT_CONFIG: {
h264: { extension: ".mp4", contentType: "video/mp4" },
vp9: { extension: ".webm", contentType: "video/webm" },
vp8: { extension: ".webm", contentType: "video/webm" },
},
}));
@@ -311,7 +311,7 @@ describe("serveStaticProjectHtml transparent media proxies", () => {
},
);
it("serves an alpha-bearing video through a VP9 WebM proxy", async () => {
it("serves an alpha-bearing video through a VP8 WebM proxy", async () => {
const projectDir = mk();
writeFileSync(join(projectDir, "clip.mov"), "prores-4444-alpha-bytes");
mocks.probeAssetCodec.mockResolvedValueOnce({
@@ -321,7 +321,7 @@ describe("serveStaticProjectHtml transparent media proxies", () => {
representativeMime: null,
});
const proxyPath = join(projectDir, "proxy.webm");
writeFileSync(proxyPath, "vp9-alpha-proxy");
writeFileSync(proxyPath, "vp8-alpha-proxy");
mocks.resolveProxy.mockResolvedValueOnce(proxyPath);
server = await serveStaticProjectHtml(projectDir, "<html></html>");
@@ -333,7 +333,7 @@ describe("serveStaticProjectHtml transparent media proxies", () => {
expect(mocks.resolveProxy).toHaveBeenCalledWith(
projectDir,
join(projectDir, "clip.mov"),
"vp9",
"vp8",
);
});