Skip to content

Commit

Permalink
⚡ (video) Add Tiktok videos support
Browse files Browse the repository at this point in the history
Closes #936
  • Loading branch information
baptisteArno committed Nov 9, 2023
1 parent df57841 commit 25c7044
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { useTranslate } from '@tolgee/react'
import { Box, Text, Image } from '@chakra-ui/react'
import { VideoBubbleBlock } from '@typebot.io/schemas'
import { VideoBubbleContentType } from '@typebot.io/schemas/features/blocks/bubbles/video/constants'
import {
VideoBubbleContentType,
vimeoBaseUrl,
youtubeBaseUrl,
} from '@typebot.io/schemas/features/blocks/bubbles/video/constants'

type Props = {
block: VideoBubbleBlock
Expand Down Expand Up @@ -49,13 +53,30 @@ export const VideoBubbleContent = ({ block }: Props) => {
case VideoBubbleContentType.YOUTUBE: {
const baseUrl =
block.content.type === VideoBubbleContentType.VIMEO
? 'https://player.vimeo.com/video'
: 'https://www.youtube.com/embed'
? vimeoBaseUrl
: youtubeBaseUrl
return (
<Box w="full" h="120px" pos="relative">
<iframe
src={`${baseUrl}/${block.content.id}`}
allowFullScreen
style={{
width: '100%',
height: '100%',
position: 'absolute',
left: '0',
top: '0',
borderRadius: '10px',
pointerEvents: 'none',
}}
/>
</Box>
)
}
case VideoBubbleContentType.TIKTOK: {
return (
<Box w="full" h="300px" pos="relative">
<iframe
src={`https://www.tiktok.com/embed/v2/${block.content.id}`}
style={{
width: '100%',
height: '100%',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const VideoUploadContent = ({ content, onSubmit }: Props) => {
const info = parseVideoUrl(url)
return onSubmit({
type: info.type,
url,
url: info.url,
id: info.id,
})
}
Expand Down
1 change: 1 addition & 0 deletions packages/bot-engine/parseBubbleBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export const parseBubbleBlock = (
const parsedContent = block.content
? deepParseVariables(variables)(block.content)
: undefined

return {
...block,
content: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@ import { createSignal, Match, onCleanup, onMount, Switch } from 'solid-js'
import { clsx } from 'clsx'
import {
defaultVideoBubbleContent,
embeddableVideoTypes,
tiktokBaseUrl,
VideoBubbleContentType,
vimeoBaseUrl,
youtubeBaseUrl,
} from '@typebot.io/schemas/features/blocks/bubbles/video/constants'
import { VideoBubbleBlock } from '@typebot.io/schemas'
import {
EmbeddableVideoBubbleContentType,
VideoBubbleBlock,
} from '@typebot.io/schemas'

type Props = {
content: VideoBubbleBlock['content']
Expand All @@ -23,8 +30,8 @@ export const VideoBubble = (props: Props) => {
onMount(() => {
const typingDuration =
props.content?.type &&
[VideoBubbleContentType.VIMEO, VideoBubbleContentType.YOUTUBE].includes(
props.content?.type
embeddableVideoTypes.includes(
props.content?.type as EmbeddableVideoBubbleContentType
)
? 2000
: 100
Expand Down Expand Up @@ -77,10 +84,9 @@ export const VideoBubble = (props: Props) => {
<Match
when={
props.content?.type &&
[
VideoBubbleContentType.VIMEO,
VideoBubbleContentType.YOUTUBE,
].includes(props.content.type)
embeddableVideoTypes.includes(
props.content.type as EmbeddableVideoBubbleContentType
)
}
>
<div
Expand All @@ -100,11 +106,9 @@ export const VideoBubble = (props: Props) => {
}}
>
<iframe
src={`${
props.content?.type === VideoBubbleContentType.VIMEO
? 'https://player.vimeo.com/video'
: 'https://www.youtube.com/embed'
}/${props.content?.id}`}
src={`${getBaseUrl(
props.content?.type as EmbeddableVideoBubbleContentType
)}/${props.content?.id}`}
class={'w-full h-full'}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
Expand All @@ -117,3 +121,14 @@ export const VideoBubble = (props: Props) => {
</div>
)
}

const getBaseUrl = (type: EmbeddableVideoBubbleContentType): string => {
switch (type) {
case VideoBubbleContentType.VIMEO:
return vimeoBaseUrl
case VideoBubbleContentType.YOUTUBE:
return youtubeBaseUrl
case VideoBubbleContentType.TIKTOK:
return tiktokBaseUrl
}
}
39 changes: 27 additions & 12 deletions packages/lib/parseVideoUrl.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
import { VideoBubbleContentType } from '@typebot.io/schemas/features/blocks/bubbles/video/constants'

const vimeoRegex = /vimeo\.com\/(\d+)/
const youtubeRegex =
/youtube\.com\/(watch\?v=|shorts\/)([\w-]+)|youtu\.be\/([\w-]+)/
import {
VideoBubbleContentType,
tiktokBaseUrl,
tiktokRegex,
vimeoBaseUrl,
vimeoRegex,
youtubeBaseUrl,
youtubeRegex,
} from '@typebot.io/schemas/features/blocks/bubbles/video/constants'

export const parseVideoUrl = (
url: string
): { type: VideoBubbleContentType; url: string; id?: string } => {
if (youtubeRegex.test(url)) {
const match = url.match(youtubeRegex)
const id = match?.at(2) ?? match?.at(3)
const parsedUrl = match?.at(0) ?? url
if (!id) return { type: VideoBubbleContentType.URL, url: parsedUrl }
return { type: VideoBubbleContentType.YOUTUBE, url: parsedUrl, id }
}
if (vimeoRegex.test(url)) {
const id = url.match(vimeoRegex)?.at(1)
if (!id) return { type: VideoBubbleContentType.URL, url }
return { type: VideoBubbleContentType.VIMEO, url, id }
const match = url.match(vimeoRegex)
const id = match?.at(1)
const parsedUrl = match?.at(0) ?? url
if (!id) return { type: VideoBubbleContentType.URL, url: parsedUrl }
return { type: VideoBubbleContentType.VIMEO, url: parsedUrl, id }
}
if (youtubeRegex.test(url)) {
const id = url.match(youtubeRegex)?.at(2) ?? url.match(youtubeRegex)?.at(3)
if (!id) return { type: VideoBubbleContentType.URL, url }
return { type: VideoBubbleContentType.YOUTUBE, url, id }
if (tiktokRegex.test(url)) {
const match = url.match(tiktokRegex)
const id = url.match(tiktokRegex)?.at(1)
const parsedUrl = match?.at(0) ?? url
if (!id) return { type: VideoBubbleContentType.URL, url: parsedUrl }
return { type: VideoBubbleContentType.TIKTOK, url: parsedUrl, id }
}
return { type: VideoBubbleContentType.URL, url }
}
17 changes: 17 additions & 0 deletions packages/schemas/features/blocks/bubbles/video/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,25 @@ export enum VideoBubbleContentType {
URL = 'url',
YOUTUBE = 'youtube',
VIMEO = 'vimeo',
TIKTOK = 'tiktok',
}

export const embeddableVideoTypes = [
VideoBubbleContentType.YOUTUBE,
VideoBubbleContentType.VIMEO,
VideoBubbleContentType.TIKTOK,
] as const

export const defaultVideoBubbleContent = {
height: 400,
} as const

export const youtubeBaseUrl = 'https://www.youtube.com/embed'
export const youtubeRegex =
/youtube\.com\/(watch\?v=|shorts\/)([\w-]+)|youtu\.be\/([\w-]+)/

export const vimeoBaseUrl = 'https://player.vimeo.com/video'
export const vimeoRegex = /vimeo\.com\/(\d+)/

export const tiktokBaseUrl = 'https://www.tiktok.com/embed/v2'
export const tiktokRegex = /tiktok\.com\/@[\w-]+\/video\/(\d+)/
4 changes: 4 additions & 0 deletions packages/schemas/features/blocks/bubbles/video/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ export const videoBubbleBlockSchema = blockBaseSchema.merge(
)

export type VideoBubbleBlock = z.infer<typeof videoBubbleBlockSchema>
export type EmbeddableVideoBubbleContentType = Exclude<
VideoBubbleContentType,
VideoBubbleContentType.URL
>

0 comments on commit 25c7044

Please sign in to comment.