@ -24,3 +24,21 @@ The Video bubble block allows you to display a video to your user. You can paste
|
|||||||
></video>
|
></video>
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
## Video service detection
|
||||||
|
|
||||||
|
When you paste a video URL, Typbot will automatically detect the video service and parse it with the proper iframe settings. It currently works with:
|
||||||
|
|
||||||
|
- YouTube (video and shorts)
|
||||||
|
- Vimeo
|
||||||
|
- TikTok
|
||||||
|
- Gumlet
|
||||||
|
- OneDrive
|
||||||
|
|
||||||
|
Feel free to [suggest a missing service in the feedback board](https://feedback.typebot.io/).
|
||||||
|
|
||||||
|
For missing services, you can instead add an Embed bubble and paste the iframe code.
|
||||||
|
|
||||||
|
### Limitations
|
||||||
|
|
||||||
|
- YouTube clips are not supported as we can't transform it to embed URL automatically. To embed a YouTube clip, you can use the [Embed bubble](/editor/blocks/bubbles/embed) and paste the iframe code that can be found in Youtube under the "Share" > "Embed" button.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/js",
|
"name": "@typebot.io/js",
|
||||||
"version": "0.2.91",
|
"version": "0.2.92",
|
||||||
"description": "Javascript library to display typebots on your website",
|
"description": "Javascript library to display typebots on your website",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
@ -128,7 +128,9 @@ export const VideoBubble = (props: Props) => {
|
|||||||
embedBaseUrls[
|
embedBaseUrls[
|
||||||
props.content?.type as EmbeddableVideoBubbleContentType
|
props.content?.type as EmbeddableVideoBubbleContentType
|
||||||
]
|
]
|
||||||
}/${props.content?.id}`}
|
}/${props.content?.id ?? ''}${
|
||||||
|
props.content?.queryParamsStr ?? ''
|
||||||
|
}`}
|
||||||
class={'w-full h-full'}
|
class={'w-full h-full'}
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
allowfullscreen
|
allowfullscreen
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/nextjs",
|
"name": "@typebot.io/nextjs",
|
||||||
"version": "0.2.91",
|
"version": "0.2.92",
|
||||||
"description": "Convenient library to display typebots on your Next.js website",
|
"description": "Convenient library to display typebots on your Next.js website",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/react",
|
"name": "@typebot.io/react",
|
||||||
"version": "0.2.91",
|
"version": "0.2.92",
|
||||||
"description": "Convenient library to display typebots on your React app",
|
"description": "Convenient library to display typebots on your React app",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
@ -31,7 +31,11 @@ export const verticalVideoSuggestionSize = {
|
|||||||
|
|
||||||
const youtubeBaseUrl = 'https://www.youtube.com/embed'
|
const youtubeBaseUrl = 'https://www.youtube.com/embed'
|
||||||
export const youtubeRegex =
|
export const youtubeRegex =
|
||||||
/youtube\.com\/(watch\?v=|shorts\/)([\w-]+)|youtu\.be\/([\w-]+)/
|
/youtube\.com\/(watch\?v=|shorts\/)([\w-]+)|youtu\.be\/([\w-]+)(\?.+)/
|
||||||
|
|
||||||
|
export const youtubeEmbedParamsMap = {
|
||||||
|
t: 'start',
|
||||||
|
}
|
||||||
|
|
||||||
const vimeoBaseUrl = 'https://player.vimeo.com/video'
|
const vimeoBaseUrl = 'https://player.vimeo.com/video'
|
||||||
export const vimeoRegex = /vimeo\.com\/(\d+)/
|
export const vimeoRegex = /vimeo\.com\/(\d+)/
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
tiktokRegex,
|
tiktokRegex,
|
||||||
gumletRegex,
|
gumletRegex,
|
||||||
oneDriveRegex,
|
oneDriveRegex,
|
||||||
|
youtubeEmbedParamsMap,
|
||||||
} from './constants'
|
} from './constants'
|
||||||
import { VideoBubbleBlock } from './schema'
|
import { VideoBubbleBlock } from './schema'
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ export const parseVideoUrl = (
|
|||||||
type: VideoBubbleContentType
|
type: VideoBubbleContentType
|
||||||
url: string
|
url: string
|
||||||
id?: string
|
id?: string
|
||||||
|
queryParamsStr?: string
|
||||||
videoSizeSuggestion?: Pick<
|
videoSizeSuggestion?: Pick<
|
||||||
NonNullable<VideoBubbleBlock['content']>,
|
NonNullable<VideoBubbleBlock['content']>,
|
||||||
'aspectRatio' | 'maxWidth'
|
'aspectRatio' | 'maxWidth'
|
||||||
@ -24,12 +26,22 @@ export const parseVideoUrl = (
|
|||||||
if (youtubeRegex.test(url)) {
|
if (youtubeRegex.test(url)) {
|
||||||
const match = url.match(youtubeRegex)
|
const match = url.match(youtubeRegex)
|
||||||
const id = match?.at(2) ?? match?.at(3)
|
const id = match?.at(2) ?? match?.at(3)
|
||||||
|
const queryParams = match?.at(4)
|
||||||
|
? new URLSearchParams(match.at(4))
|
||||||
|
: undefined
|
||||||
|
Object.entries(youtubeEmbedParamsMap).forEach(([key, value]) => {
|
||||||
|
if (queryParams?.has(key)) {
|
||||||
|
queryParams.set(value, queryParams.get(key)!)
|
||||||
|
queryParams.delete(key)
|
||||||
|
}
|
||||||
|
})
|
||||||
const parsedUrl = match?.at(0) ?? url
|
const parsedUrl = match?.at(0) ?? url
|
||||||
if (!id) return { type: VideoBubbleContentType.URL, url: parsedUrl }
|
if (!id) return { type: VideoBubbleContentType.URL, url: parsedUrl }
|
||||||
return {
|
return {
|
||||||
type: VideoBubbleContentType.YOUTUBE,
|
type: VideoBubbleContentType.YOUTUBE,
|
||||||
url: parsedUrl,
|
url: parsedUrl,
|
||||||
id,
|
id,
|
||||||
|
queryParamsStr: queryParams ? '?' + queryParams.toString() : undefined,
|
||||||
videoSizeSuggestion: url.includes('shorts')
|
videoSizeSuggestion: url.includes('shorts')
|
||||||
? verticalVideoSuggestionSize
|
? verticalVideoSuggestionSize
|
||||||
: horizontalVideoSuggestionSize,
|
: horizontalVideoSuggestionSize,
|
||||||
|
@ -11,6 +11,7 @@ export const videoBubbleContentSchema = z.object({
|
|||||||
height: z.number().or(variableStringSchema).optional(),
|
height: z.number().or(variableStringSchema).optional(),
|
||||||
aspectRatio: z.string().optional(),
|
aspectRatio: z.string().optional(),
|
||||||
maxWidth: z.string().optional(),
|
maxWidth: z.string().optional(),
|
||||||
|
queryParamsStr: z.string().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const videoBubbleBlockSchema = blockBaseSchema.merge(
|
export const videoBubbleBlockSchema = blockBaseSchema.merge(
|
||||||
|
Reference in New Issue
Block a user