fix: video embed url (#4485)

Co-authored-by: Dhruwang <dhruwangjariwala18@gmail.com>
This commit is contained in:
Piyush Gupta
2024-12-18 10:17:21 +05:30
committed by GitHub
parent 08f22983e7
commit a0d02a843e
7 changed files with 59 additions and 60 deletions

View File

@@ -2,10 +2,10 @@ import { AdvancedOptionToggle } from "@/modules/ui/components/advanced-option-to
import { Button } from "@/modules/ui/components/button";
import { Input } from "@/modules/ui/components/input";
import { AlertTriangle } from "lucide-react";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { toast } from "react-hot-toast";
import { checkForYoutubeUrl } from "@formbricks/lib/utils/videoUpload";
import { extractYoutubeId, parseVideoUrl } from "@formbricks/lib/utils/videoUpload";
import { checkForYoutubeUrl, convertToEmbedUrl, extractYoutubeId } from "@formbricks/lib/utils/videoUpload";
import { Label } from "../../label";
import { checkForYoutubePrivacyMode } from "../lib/utils";
@@ -24,6 +24,7 @@ export const VideoSettings = ({
videoUrl,
setVideoUrlTemp,
}: VideoSettingsProps) => {
const t = useTranslations();
const [isYoutubeLink, setIsYoutubeLink] = useState(checkForYoutubeUrl(uploadedVideoUrl));
const [isYoutubePrivacyModeEnabled, setIsYoutubePrivacyModeEnabled] = useState(
checkForYoutubePrivacyMode(uploadedVideoUrl)
@@ -34,7 +35,7 @@ export const VideoSettings = ({
const videoId = extractYoutubeId(uploadedVideoUrl);
if (!videoId) {
toast.error("Invalid YouTube URL");
toast.error(t("environments.surveys.edit.invalid_youtube_url"));
return;
}
const newUrl = isYoutubePrivacyModeEnabled
@@ -49,24 +50,26 @@ export const VideoSettings = ({
const handleAddVideo = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
e.preventDefault();
const parsedUrl = parseVideoUrl(uploadedVideoUrl);
if (parsedUrl) {
setUploadedVideoUrl(parsedUrl);
onFileUpload([parsedUrl], "video");
const embedUrl = convertToEmbedUrl(uploadedVideoUrl.trim());
if (embedUrl) {
setUploadedVideoUrl(embedUrl);
onFileUpload([embedUrl], "video");
} else {
toast.error("Url not supported");
toast.error(t("environments.surveys.edit.url_not_supported"));
}
};
const handleRemoveVideo = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
e.preventDefault();
setVideoUrlTemp("");
setUploadedVideoUrl("");
onFileUpload([], "video");
};
const handleVideoUrlChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const videoUrl = e.target.value;
setUploadedVideoUrl(videoUrl);
// Check if the URL is from one of the supported platforms
const isSupportedPlatform = [
"youtube.com",
@@ -82,26 +85,26 @@ export const VideoSettings = ({
};
const isAddButtonDisabled = () => {
return uploadedVideoUrl.trim() !== "" ? false : true;
return uploadedVideoUrl.trim() === "";
};
return (
<form className="flex flex-col space-y-4">
<Label>Video URL(Youtube, Vimeo or Loom):</Label>
<div className="flex flex-col space-y-4">
<Label>Video URL (YouTube, Vimeo, or Loom):</Label>
<div className="flex h-10 items-center space-x-2">
<Input
className="w-full"
placeholder="https://www.youtube.com/embed/VIDEO_ID"
placeholder="https://www.youtube.com/watch?v=VIDEO_ID"
value={uploadedVideoUrl}
onChange={(e) => handleVideoUrlChange(e)}
onChange={handleVideoUrlChange}
/>
{uploadedVideoUrl && videoUrl === uploadedVideoUrl ? (
<Button variant="secondary" onClick={(e) => handleRemoveVideo(e)}>
Remove
<Button variant="secondary" onClick={handleRemoveVideo}>
{t("common.remove")}
</Button>
) : (
<Button onClick={(e) => handleAddVideo(e)} disabled={isAddButtonDisabled()}>
Add
<Button onClick={handleAddVideo} disabled={isAddButtonDisabled()}>
{t("common.add")}
</Button>
)}
</div>
@@ -109,23 +112,19 @@ export const VideoSettings = ({
{showPlatformWarning && (
<div className="flex items-center space-x-2 rounded-md border bg-slate-100 p-2 text-xs text-slate-600">
<AlertTriangle className="h-6 w-6" />
<p>
Please enter a valid Youtube, Vimeo or Loom Url. We currently do not support other video hosting
providers.
</p>
<p>{t("environments.surveys.edit.invalid_video_url_warning")}</p>
</div>
)}
{isYoutubeLink && (
<AdvancedOptionToggle
htmlId="closeOnNumberOfResponse"
htmlId="youtubePrivacyMode"
isChecked={isYoutubePrivacyModeEnabled}
onToggle={() => {
toggleYoutubePrivacyMode();
}}
onToggle={toggleYoutubePrivacyMode}
title="YouTube Privacy Mode"
description="Keeps user tracking to a minimum"
childBorder={true}></AdvancedOptionToggle>
)}
</form>
</div>
);
};

View File

@@ -1504,6 +1504,8 @@
"input_color": "Farbe des Eingabefelds",
"invalid_segment": "Ungültiges Segment",
"invalid_targeting": "Ungültiges Targeting: Bitte überprüfe deine Zielgruppenfilter",
"invalid_video_url_warning": "Bitte gib eine gültige YouTube-, Vimeo- oder Loom-URL ein. Andere Video-Plattformen werden derzeit nicht unterstützt.",
"invalid_youtube_url": "Ungültige YouTube-URL",
"is_after": "Ist nach",
"is_before": "Ist vor",
"is_booked": "Ist gebucht",
@@ -1674,6 +1676,7 @@
"upper_label": "Oberes Label",
"url_encryption": "URL-Verschlüsselung",
"url_filters": "URL-Filter",
"url_not_supported": "URL nicht unterstützt",
"use_with_caution": "Mit Vorsicht verwenden",
"user_targeting_is_currently_only_available_when": "Benutzerzielgruppen sind derzeit nur verfügbar, wenn",
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} wird in der Logik der Frage {questionIndex} verwendet. Bitte entferne es zuerst aus der Logik.",

View File

@@ -1504,6 +1504,8 @@
"input_color": "Input color",
"invalid_segment": "Invalid segment",
"invalid_targeting": "Invalid targeting: Please check your audience filters",
"invalid_video_url_warning": "Please enter a valid YouTube, Vimeo, or Loom URL. We currently do not support other video hosting providers.",
"invalid_youtube_url": "Invalid YouTube URL",
"is_after": "Is after",
"is_before": "Is before",
"is_booked": "Is booked",
@@ -1674,6 +1676,7 @@
"upper_label": "Upper Label",
"url_encryption": "URL Encryption",
"url_filters": "URL Filters",
"url_not_supported": "URL not supported",
"use_with_caution": "Use with caution",
"user_targeting_is_currently_only_available_when": "User targeting is currently only available when",
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} is used in logic of question {questionIndex}. Please remove it from logic first.",

View File

@@ -1504,6 +1504,8 @@
"input_color": "Couleur d'entrée",
"invalid_segment": "Segment invalide",
"invalid_targeting": "Ciblage invalide : Veuillez vérifier vos filtres d'audience",
"invalid_video_url_warning": "Merci d'entrer une URL YouTube, Vimeo ou Loom valide. Les autres plateformes vidéo ne sont pas encore supportées.",
"invalid_youtube_url": "URL YouTube invalide",
"is_after": "est après",
"is_before": "Est avant",
"is_booked": "Est réservé",
@@ -1674,6 +1676,7 @@
"upper_label": "Étiquette supérieure",
"url_encryption": "Chiffrement d'URL",
"url_filters": "Filtres d'URL",
"url_not_supported": "URL non supportée",
"use_with_caution": "À utiliser avec précaution",
"user_targeting_is_currently_only_available_when": "La ciblage des utilisateurs est actuellement disponible uniquement lorsque",
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} est utilisé dans la logique de la question {questionIndex}. Veuillez d'abord le supprimer de la logique.",

View File

@@ -1504,6 +1504,8 @@
"input_color": "Cor de entrada",
"invalid_segment": "Segmento inválido",
"invalid_targeting": "Segmentação inválida: Por favor, verifique os filtros do seu público",
"invalid_video_url_warning": "Por favor, insira uma URL válida do YouTube, Vimeo ou Loom. No momento, não suportamos outros provedores de vídeo.",
"invalid_youtube_url": "URL do YouTube inválida",
"is_after": "é depois",
"is_before": "é antes",
"is_booked": "Tá reservado",
@@ -1674,6 +1676,7 @@
"upper_label": "Etiqueta Superior",
"url_encryption": "Criptografia de URL",
"url_filters": "Filtros de URL",
"url_not_supported": "URL não suportada",
"use_with_caution": "Use com cuidado",
"user_targeting_is_currently_only_available_when": "A segmentação de usuários está disponível atualmente apenas quando",
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} está sendo usado na lógica da pergunta {questionIndex}. Por favor, remova-o da lógica primeiro.",

View File

@@ -73,7 +73,7 @@ export const extractYoutubeId = (url: string): string | null => {
return false;
});
return id;
return id || null;
};
const extractVimeoId = (url: string): string | null => {
@@ -96,44 +96,32 @@ const extractLoomId = (url: string): string | null => {
return null;
};
// Function to convert watch urls into embed urls and vice versa
export const parseVideoUrl = (url: string): string | undefined => {
// YouTube URL handling
// Always convert a given URL into its embed form if supported.
export const convertToEmbedUrl = (url: string): string | undefined => {
// YouTube
if (checkForYoutubeUrl(url)) {
if (url.includes("/embed/")) {
// Reverse parse for YouTube embed URLs
const videoId = url.split("/embed/")[1].split("?")[0];
return `https://www.youtube.com/watch?v=${videoId}`;
} else {
// Normal parse for YouTube URLs
const videoId = extractYoutubeId(url);
if (videoId) {
return `https://www.youtube.com/embed/${videoId}`;
}
const videoId = extractYoutubeId(url);
if (videoId) {
return `https://www.youtube.com/embed/${videoId}`;
}
}
// Vimeo URL handling
else if (checkForVimeoUrl(url)) {
if (url.includes("/video/")) {
// Reverse parse for Vimeo embed URLs
const videoId = url.split("/video/")[1].split("?")[0];
return `https://www.vimeo.com/${videoId}`;
} else {
// Normal parse for Vimeo URLs
const videoId = extractVimeoId(url);
// Vimeo
if (checkForVimeoUrl(url)) {
const videoId = extractVimeoId(url);
if (videoId) {
return `https://player.vimeo.com/video/${videoId}`;
}
}
// Loom URL handling
else if (checkForLoomUrl(url)) {
if (url.includes("/embed/")) {
// Reverse parse for Loom embed URLs
const videoId = url.split("/embed/")[1].split("?")[0];
return `https://www.loom.com/share/${videoId}`;
} else {
// Normal parse for Loom URLs
const videoId = extractLoomId(url);
// Loom
if (checkForLoomUrl(url)) {
const videoId = extractLoomId(url);
if (videoId) {
return `https://www.loom.com/embed/${videoId}`;
}
}
// If no supported platform found, return undefined
return undefined;
};

View File

@@ -3,7 +3,7 @@ import {
checkForLoomUrl,
checkForVimeoUrl,
checkForYoutubeUrl,
parseVideoUrl,
convertToEmbedUrl,
} from "@formbricks/lib/utils/videoUpload";
//Function to add extra params to videoUrls in order to reduce video controls
@@ -64,7 +64,7 @@ export function QuestionMedia({ imgUrl, videoUrl, altText = "Image" }: QuestionM
</div>
) : null}
<a
href={imgUrl ? imgUrl : parseVideoUrl(videoUrl ?? "")}
href={imgUrl ? imgUrl : convertToEmbedUrl(videoUrl ?? "")}
target="_blank"
rel="noreferrer"
className="fb-absolute fb-bottom-2 fb-right-2 fb-flex fb-items-center fb-gap-2 fb-rounded-md fb-bg-slate-800 fb-bg-opacity-40 fb-p-1.5 fb-text-white fb-opacity-0 fb-backdrop-blur-lg fb-transition fb-duration-300 fb-ease-in-out hover:fb-bg-opacity-65 group-hover/image:fb-opacity-100">