mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
feat(media): alpha-capable authoring proxies (#2598)
* feat(media): alpha-capable authoring proxies Alpha sources were refused a proxy before the codec map ever asked whether the browser could decode them, so a ProRes 4444 alpha file (which no browser previews at all) rendered black forever, while an alpha WebM (which previews fine) was already covered by the browser-safe check on the next line. The alpha veto earned nothing and cost the one case that needed help. Alpha is now a target-codec choice rather than a veto: alpha sources transcode to VP9 + yuva420p in WebM, everything else keeps the existing H.264/MP4 path byte for byte. Only files no browser can preview are proxied, which is the rule the runtime already followed everywhere else. WebM cannot carry AAC, so the VP9 path uses Opus and drops the MP4-only faststart flag. PROXY_PARAMS_VERSION moves to v3 so clients stop serving the previously cached proxies. Safari does not decode VP9 alpha and still shows black for alpha sources, as it does today: this is better on Chromium and Firefox and no worse anywhere. * fix(media): infer proxy variant for rescue * fix(media): preserve alpha proxy hardening after restack
This commit is contained in:
@@ -59,10 +59,20 @@ const mediaMocks = vi.hoisted(() => ({
|
||||
representativeMime: "video/mp4",
|
||||
hasAlpha: false,
|
||||
})),
|
||||
decideMediaProxyEligibility: vi.fn(
|
||||
(facts: { hasAlpha: boolean; browserHostile: boolean } | null) =>
|
||||
facts?.hasAlpha ? { eligible: false, reason: "alpha_source" } : { eligible: true },
|
||||
decideMediaProxyEligibility: vi.fn((facts: { browserHostile: boolean } | null) =>
|
||||
facts?.browserHostile ? { eligible: true } : { eligible: false, reason: "browser_safe_codec" },
|
||||
),
|
||||
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";
|
||||
return request === "auto" || request === expected ? expected : null;
|
||||
},
|
||||
PROXY_VARIANT_CONFIG: {
|
||||
h264: { extension: ".mp4", contentType: "video/mp4" },
|
||||
vp9: { extension: ".webm", contentType: "video/webm" },
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@hyperframes/studio-server/proxy-transcoder", () => ({
|
||||
@@ -109,7 +119,7 @@ afterEach(() => {
|
||||
dir = undefined;
|
||||
});
|
||||
|
||||
it("rejects direct proxy requests for alpha sources", async () => {
|
||||
it("serves direct VP9 proxy requests for alpha sources", async () => {
|
||||
const project = tmpProject();
|
||||
writeFileSync(join(project.dir, "clip.mov"), "alpha-prores");
|
||||
mediaMocks.probeAssetCodec.mockResolvedValueOnce({
|
||||
@@ -118,17 +128,20 @@ it("rejects direct proxy requests for alpha sources", async () => {
|
||||
representativeMime: "video/quicktime",
|
||||
hasAlpha: true,
|
||||
});
|
||||
mediaMocks.decideMediaProxyEligibility.mockReturnValueOnce({
|
||||
eligible: false,
|
||||
reason: "alpha_source",
|
||||
});
|
||||
const proxyPath = join(project.dir, "proxy.webm");
|
||||
writeFileSync(proxyPath, "vp9-alpha-proxy");
|
||||
mocks.resolveProxy.mockResolvedValueOnce(proxyPath);
|
||||
const app = await buildApp(project, true);
|
||||
|
||||
const res = await app.request("/composition/clip.mov?hf-proxy=h264");
|
||||
const res = await app.request("/composition/clip.mov?hf-proxy=auto");
|
||||
|
||||
expect(res.status).toBe(422);
|
||||
expect(await res.text()).toContain("alpha_source");
|
||||
expect(mocks.resolveProxy).not.toHaveBeenCalled();
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.headers.get("content-type")).toBe("video/webm");
|
||||
expect(mocks.resolveProxy).toHaveBeenCalledWith(
|
||||
project.dir,
|
||||
join(project.dir, "clip.mov"),
|
||||
"vp9",
|
||||
);
|
||||
});
|
||||
|
||||
async function buildApp(project: ProjectDir, autoProxy: boolean): Promise<Hono> {
|
||||
@@ -163,7 +176,11 @@ describe("registerCompositionRoute", () => {
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(await res.text()).toBe("transcoded-h264-bytes");
|
||||
expect(mocks.resolveProxy).toHaveBeenCalledWith(project.dir, join(project.dir, "clip.mp4"));
|
||||
expect(mocks.resolveProxy).toHaveBeenCalledWith(
|
||||
project.dir,
|
||||
join(project.dir, "clip.mp4"),
|
||||
"h264",
|
||||
);
|
||||
});
|
||||
|
||||
it("serves ?hf-proxy=h264 for a .mov hostile asset as Content-Type video/mp4 (the proxy IS mp4)", async () => {
|
||||
|
||||
Reference in New Issue
Block a user