mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 07:09:59 +00:00
fix(studio): route rooted timeline media through preview (#3061)
This commit is contained in:
@@ -51,6 +51,34 @@ describe("computeThumbnailStrip", () => {
|
||||
});
|
||||
|
||||
describe("resolveMediaPreviewUrl", () => {
|
||||
it("reroutes same-origin root media resolved by the preview iframe", () => {
|
||||
expect(
|
||||
resolveMediaPreviewUrl(
|
||||
"http://localhost:5190/assets/clip.mp4",
|
||||
"proj-1",
|
||||
"http://localhost:5190",
|
||||
),
|
||||
).toBe("/api/projects/proj-1/preview/assets/clip.mp4");
|
||||
});
|
||||
|
||||
it("preserves empty, canonical preview, and same-origin API sources", () => {
|
||||
expect(resolveMediaPreviewUrl("", "proj-1", "http://localhost:5190")).toBe("");
|
||||
expect(
|
||||
resolveMediaPreviewUrl(
|
||||
"http://localhost:5190/api/projects/proj-1/preview/assets/clip.mp4",
|
||||
"proj-1",
|
||||
"http://localhost:5190",
|
||||
),
|
||||
).toBe("http://localhost:5190/api/projects/proj-1/preview/assets/clip.mp4");
|
||||
expect(
|
||||
resolveMediaPreviewUrl(
|
||||
"http://localhost:5190/api/media/clip.mp4",
|
||||
"proj-1",
|
||||
"http://localhost:5190",
|
||||
),
|
||||
).toBe("http://localhost:5190/api/media/clip.mp4");
|
||||
});
|
||||
|
||||
it("routes composition-relative paths through the project preview endpoint", () => {
|
||||
expect(resolveMediaPreviewUrl("assets/image.png", "proj-1")).toBe(
|
||||
"/api/projects/proj-1/preview/assets/image.png",
|
||||
|
||||
@@ -90,12 +90,43 @@ export function encodePreviewPath(relativePath: string): string {
|
||||
* (parent) document. Composition-relative paths (e.g. "assets/image.png") are
|
||||
* routed through the project preview endpoint with each segment encoded.
|
||||
*
|
||||
* Already-loadable URLs pass through untouched: absolute http(s) URLs, plus
|
||||
* `data:` and `blob:` URLs. Routing a `data:`/`blob:` URL through the preview
|
||||
* endpoint would percent-encode the whole thing into a multi-KB path segment
|
||||
* that the server rejects with HTTP 431 (Request Header Fields Too Large).
|
||||
* External http(s), `data:`, and `blob:` URLs pass through untouched. A
|
||||
* same-origin absolute URL outside the project preview endpoint is the browser's
|
||||
* resolved form of a root-relative authored path, so route it back through the
|
||||
* active project instead of accidentally fetching the Studio shell.
|
||||
*/
|
||||
export function resolveMediaPreviewUrl(src: string, projectId: string): string {
|
||||
if (/^(?:https?:|data:|blob:)/i.test(src)) return src;
|
||||
return `/api/projects/${projectId}/preview/${encodePreviewPath(src)}`;
|
||||
export function resolveMediaPreviewUrl(
|
||||
src: string,
|
||||
projectId: string,
|
||||
studioOrigin?: string,
|
||||
): string {
|
||||
if (!src) return src;
|
||||
if (/^(?:data:|blob:)/i.test(src)) return src;
|
||||
|
||||
let relativePath = src;
|
||||
let suffix = "";
|
||||
if (/^https?:/i.test(src)) {
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(src);
|
||||
} catch {
|
||||
return src;
|
||||
}
|
||||
if (!studioOrigin || parsed.origin !== studioOrigin) return src;
|
||||
const previewPath = new URL(`/api/projects/${projectId}/preview/`, studioOrigin).pathname;
|
||||
if (parsed.pathname.startsWith(previewPath)) return src;
|
||||
if (parsed.pathname.startsWith("/api/")) return src;
|
||||
try {
|
||||
relativePath = parsed.pathname
|
||||
.replace(/^\/+/, "")
|
||||
.split("/")
|
||||
.map(decodeURIComponent)
|
||||
.join("/");
|
||||
} catch {
|
||||
return src;
|
||||
}
|
||||
suffix = `${parsed.search}${parsed.hash}`;
|
||||
}
|
||||
|
||||
return `/api/projects/${projectId}/preview/${encodePreviewPath(relativePath.replace(/^\/+/, ""))}${suffix}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user