2
0

🐛 (video) Parse youtube start time query param

Closes #1589
This commit is contained in:
Baptiste Arnaud
2024-06-24 14:30:45 +02:00
parent 9c27ca9f40
commit b10383e027
8 changed files with 42 additions and 5 deletions

View File

@ -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.

View File

@ -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",

View File

@ -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

View File

@ -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",

View File

@ -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",

View File

@ -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+)/

View File

@ -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,

View File

@ -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(