fix: vimeo + loom embed (#7018)

Co-authored-by: Dhruwang <dhruwangjariwala18@gmail.com>
This commit is contained in:
Johannes
2025-12-20 00:08:48 -08:00
committed by GitHub
parent 392a95834b
commit b70b2eef95
8 changed files with 60 additions and 30 deletions

View File

@@ -61,6 +61,9 @@ describe("convertToEmbedUrl", () => {
expect(convertToEmbedUrl("https://www.vimeo.com/123456789")).toBe(
"https://player.vimeo.com/video/123456789"
);
expect(convertToEmbedUrl("https://player.vimeo.com/video/123456789")).toBe(
"https://player.vimeo.com/video/123456789"
);
});
test("converts Loom URL to embed URL", () => {
@@ -70,6 +73,9 @@ describe("convertToEmbedUrl", () => {
expect(convertToEmbedUrl("https://loom.com/share/abcdef123456")).toBe(
"https://www.loom.com/embed/abcdef123456"
);
expect(convertToEmbedUrl("https://www.loom.com/embed/abcdef123456")).toBe(
"https://www.loom.com/embed/abcdef123456"
);
});
test("returns undefined for unsupported URLs", () => {
@@ -109,6 +115,7 @@ describe("extractVimeoId", () => {
test("extracts video ID from Vimeo URLs", () => {
expect(extractVimeoId("https://vimeo.com/123456789")).toBe("123456789");
expect(extractVimeoId("https://www.vimeo.com/123456789")).toBe("123456789");
expect(extractVimeoId("https://player.vimeo.com/video/123456789")).toBe("123456789");
});
test("returns null for invalid Vimeo URLs", () => {
@@ -121,6 +128,7 @@ describe("extractLoomId", () => {
test("extracts video ID from Loom URLs", () => {
expect(extractLoomId("https://loom.com/share/abcdef123456")).toBe("abcdef123456");
expect(extractLoomId("https://www.loom.com/share/abcdef123456")).toBe("abcdef123456");
expect(extractLoomId("https://www.loom.com/embed/abcdef123456")).toBe("abcdef123456");
});
test("returns null for invalid Loom URLs", async () => {

View File

@@ -26,7 +26,7 @@ export const checkForVimeoUrl = (url: string): boolean => {
if (vimeoUrl.protocol !== "https:") return false;
const vimeoDomains = ["www.vimeo.com", "vimeo.com"];
const vimeoDomains = ["www.vimeo.com", "vimeo.com", "player.vimeo.com"];
const hostname = vimeoUrl.hostname;
return vimeoDomains.includes(hostname);
@@ -74,7 +74,7 @@ export const extractYoutubeId = (url: string): string | null => {
};
export const extractVimeoId = (url: string): string | null => {
const regExp = /vimeo\.com\/(\d+)/;
const regExp = /vimeo\.com\/(?:video\/)?(\d+)/;
const match = regExp.exec(url);
if (match?.[1]) {
@@ -85,7 +85,7 @@ export const extractVimeoId = (url: string): string | null => {
};
export const extractLoomId = (url: string): string | null => {
const regExp = /loom\.com\/share\/([a-zA-Z0-9]+)/;
const regExp = /loom\.com\/(?:share|embed)\/([a-zA-Z0-9]+)/;
const match = regExp.exec(url);
if (match?.[1]) {

View File

@@ -138,5 +138,15 @@ describe("File Input Utils", () => {
test("returns false for non-YouTube URLs", () => {
expect(checkForYoutubePrivacyMode("https://www.example.com")).toBe(false);
});
test("should return false for empty or whitespace-only string", () => {
expect(checkForYoutubePrivacyMode("")).toBe(false);
expect(checkForYoutubePrivacyMode(" ")).toBe(false);
});
test("should return false for non-string types", () => {
expect(checkForYoutubePrivacyMode(null as any)).toBe(false);
expect(checkForYoutubePrivacyMode(123 as any)).toBe(false);
});
});
});

View File

@@ -86,6 +86,10 @@ export const getAllowedFiles = async (
};
export const checkForYoutubePrivacyMode = (url: string): boolean => {
if (!url || typeof url !== "string" || url.trim() === "") {
return false;
}
try {
const parsedUrl = new URL(url);
return parsedUrl.host === "www.youtube-nocookie.com";

View File

@@ -3,30 +3,18 @@ import * as React from "react";
import { cn } from "@/lib/utils";
import { checkForLoomUrl, checkForVimeoUrl, checkForYoutubeUrl, convertToEmbedUrl } from "@/lib/video";
// Function to add extra params to videoUrls in order to reduce video controls
const getVideoUrlWithParams = (videoUrl: string): string | undefined => {
// First convert to embed URL
const embedUrl = convertToEmbedUrl(videoUrl);
if (!embedUrl) return undefined;
//Function to add extra params to videoUrls in order to reduce video controls
const getVideoUrlWithParams = (videoUrl: string): string => {
const isYoutubeVideo = checkForYoutubeUrl(videoUrl);
const isVimeoUrl = checkForVimeoUrl(videoUrl);
const isLoomUrl = checkForLoomUrl(videoUrl);
if (isYoutubeVideo) {
// For YouTube, add parameters to embed URL
const separator = embedUrl.includes("?") ? "&" : "?";
return `${embedUrl}${separator}controls=0`;
} else if (isVimeoUrl) {
// For Vimeo, add parameters to embed URL
const separator = embedUrl.includes("?") ? "&" : "?";
return `${embedUrl}${separator}title=false&transcript=false&speed=false&quality_selector=false&progress_bar=false&pip=false&fullscreen=false&cc=false&chromecast=false`;
} else if (isLoomUrl) {
// For Loom, add parameters to embed URL
const separator = embedUrl.includes("?") ? "&" : "?";
return `${embedUrl}${separator}hide_share=true&hideEmbedTopBar=true&hide_title=true`;
}
return embedUrl;
if (isYoutubeVideo) return videoUrl.concat("?controls=0");
else if (isVimeoUrl)
return videoUrl.concat(
"?title=false&transcript=false&speed=false&quality_selector=false&progress_bar=false&pip=false&fullscreen=false&cc=false&chromecast=false"
);
else if (isLoomUrl) return videoUrl.concat("?hide_share=true&hideEmbedTopBar=true&hide_title=true");
return videoUrl;
};
interface ElementMediaProps {

View File

@@ -117,6 +117,16 @@ describe("convertToEmbedUrl", () => {
expect(result).toBe("https://player.vimeo.com/video/987654321");
});
test("handles already-embedded Vimeo URLs", () => {
const result = convertToEmbedUrl("https://player.vimeo.com/video/123456789");
expect(result).toBe("https://player.vimeo.com/video/123456789");
});
test("handles Vimeo URLs with query parameters", () => {
const result = convertToEmbedUrl("https://vimeo.com/123456789?some=param");
expect(result).toBe("https://player.vimeo.com/video/123456789");
});
test("returns undefined for invalid Vimeo URLs", () => {
const result = convertToEmbedUrl("https://www.vimeo.com/invalid");
expect(result).toBeUndefined();
@@ -134,6 +144,16 @@ describe("convertToEmbedUrl", () => {
expect(result).toBe("https://www.loom.com/embed/xyz789");
});
test("handles already-embedded Loom URLs", () => {
const result = convertToEmbedUrl("https://www.loom.com/embed/abc123def456");
expect(result).toBe("https://www.loom.com/embed/abc123def456");
});
test("handles Loom URLs with query parameters", () => {
const result = convertToEmbedUrl("https://www.loom.com/share/abc123def456?some=param");
expect(result).toBe("https://www.loom.com/embed/abc123def456");
});
test("returns undefined for invalid Loom URLs", () => {
const result = convertToEmbedUrl("https://www.loom.com/invalid");
expect(result).toBeUndefined();

View File

@@ -27,7 +27,7 @@ export const checkForVimeoUrl = (url: string): boolean => {
if (vimeoUrl.protocol !== "https:") return false;
const vimeoDomains = ["www.vimeo.com", "vimeo.com"];
const vimeoDomains = ["www.vimeo.com", "vimeo.com", "player.vimeo.com"];
const hostname = vimeoUrl.hostname;
return vimeoDomains.includes(hostname);
@@ -77,14 +77,14 @@ const extractYoutubeId = (url: string): string | null => {
};
const extractVimeoId = (url: string): string | null => {
const regExp = /vimeo\.com\/(?<videoId>\d+)/;
const regExp = /vimeo\.com\/(?:video\/)?(?<videoId>\d+)/;
const match = regExp.exec(url);
return match?.groups?.videoId ?? null;
};
const extractLoomId = (url: string): string | null => {
const regExp = /loom\.com\/share\/(?<videoId>[a-zA-Z0-9]+)/;
const regExp = /loom\.com\/(?:share|embed)\/(?<videoId>[a-zA-Z0-9]+)/;
const match = regExp.exec(url);
return match?.groups?.videoId ?? null;

View File

@@ -27,7 +27,7 @@ export const checkForVimeoUrl = (url: string): boolean => {
if (vimeoUrl.protocol !== "https:") return false;
const vimeoDomains = ["www.vimeo.com", "vimeo.com"];
const vimeoDomains = ["www.vimeo.com", "vimeo.com", "player.vimeo.com"];
const hostname = vimeoUrl.hostname;
return vimeoDomains.includes(hostname);
@@ -77,7 +77,7 @@ export const extractYoutubeId = (url: string): string | null => {
};
const extractVimeoId = (url: string): string | null => {
const regExp = /vimeo\.com\/(\d+)/;
const regExp = /vimeo\.com\/(?:video\/)?(\d+)/;
const match = url.match(regExp);
if (match && match[1]) {
@@ -87,7 +87,7 @@ const extractVimeoId = (url: string): string | null => {
};
const extractLoomId = (url: string): string | null => {
const regExp = /loom\.com\/share\/([a-zA-Z0-9]+)/;
const regExp = /loom\.com\/(?:share|embed)\/([a-zA-Z0-9]+)/;
const match = url.match(regExp);
if (match && match[1]) {