MediaBlock.tsx70 lines · main
1import type { GoImage, GoVideo } from '../schemas'
2
3interface MediaBlockProps {
4 image?: GoImage
5 video?: GoVideo
6 youtubeUrl?: string
7 className?: string
8}
9
10function getYouTubeEmbedUrl(url: string): string | null {
11 try {
12 const parsed = new URL(url)
13 let id: string | null = null
14
15 if (parsed.hostname === 'youtu.be') {
16 id = parsed.pathname.slice(1)
17 } else if (parsed.hostname === 'www.youtube.com' || parsed.hostname === 'youtube.com') {
18 if (parsed.pathname.startsWith('/embed/')) {
19 id = parsed.pathname.split('/embed/')[1]
20 } else {
21 id = parsed.searchParams.get('v')
22 }
23 }
24
25 return id ? `https://www.youtube.com/embed/${id}` : null
26 } catch {
27 return null
28 }
29}
30
31export default function MediaBlock({ image, video, youtubeUrl, className }: MediaBlockProps) {
32 const embedUrl = youtubeUrl ? getYouTubeEmbedUrl(youtubeUrl) : null
33
34 const media = image ? (
35 <img
36 src={image.src}
37 alt={image.alt}
38 width={image.width}
39 height={image.height}
40 className="w-full h-auto rounded-xl border"
41 />
42 ) : video ? (
43 <video
44 src={video.src}
45 poster={video.poster}
46 autoPlay
47 muted
48 loop
49 playsInline
50 className="w-full h-auto rounded-xl border"
51 />
52 ) : embedUrl ? (
53 <div
54 className="relative w-full rounded-xl border overflow-hidden"
55 style={{ paddingBottom: '56.25%' }}
56 >
57 <iframe
58 src={embedUrl}
59 title="YouTube video"
60 allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
61 allowFullScreen
62 className="absolute inset-0 w-full h-full"
63 />
64 </div>
65 ) : null
66
67 if (!media) return null
68
69 return <div className={className}>{media}</div>
70}