index.tsx raw
1 import { useContentPolicy } from '@/providers/ContentPolicyProvider'
2 import { useEffect, useState } from 'react'
3 import { useTranslation } from 'react-i18next'
4 import AudioPlayer from '../AudioPlayer'
5 import VideoPlayer from '../VideoPlayer'
6 import ExternalLink from '../ExternalLink'
7
8 export default function MediaPlayer({
9 src,
10 className,
11 mustLoad = false
12 }: {
13 src: string
14 className?: string
15 mustLoad?: boolean
16 }) {
17 const { t } = useTranslation()
18 const { autoLoadMedia } = useContentPolicy()
19 const [display, setDisplay] = useState(autoLoadMedia)
20 const [mediaType, setMediaType] = useState<'video' | 'audio' | null>(null)
21 const [error, setError] = useState(false)
22
23 useEffect(() => {
24 if (autoLoadMedia) {
25 setDisplay(true)
26 } else {
27 setDisplay(false)
28 }
29 }, [autoLoadMedia])
30
31 useEffect(() => {
32 if (!mustLoad && !display) {
33 setMediaType(null)
34 return
35 }
36 if (!src) {
37 setMediaType(null)
38 return
39 }
40
41 const url = new URL(src)
42 const extension = url.pathname.split('.').pop()?.toLowerCase()
43
44 if (extension && ['mp3', 'wav', 'flac', 'aac', 'm4a', 'opus', 'wma'].includes(extension)) {
45 setMediaType('audio')
46 return
47 }
48
49 const video = document.createElement('video')
50 video.src = src
51 video.preload = 'metadata'
52 video.crossOrigin = 'anonymous'
53
54 video.onloadedmetadata = () => {
55 setError(false)
56 setMediaType(video.videoWidth > 0 || video.videoHeight > 0 ? 'video' : 'audio')
57 }
58
59 video.onerror = () => {
60 setError(true)
61 }
62
63 return () => {
64 video.src = ''
65 }
66 }, [src, display, mustLoad])
67
68 if (error) {
69 return <ExternalLink url={src} />
70 }
71
72 if (!mustLoad && !display) {
73 return (
74 <div
75 className="text-primary hover:underline truncate w-fit cursor-pointer"
76 onClick={(e) => {
77 e.stopPropagation()
78 setDisplay(true)
79 }}
80 >
81 [{t('Click to load media')}]
82 </div>
83 )
84 }
85
86 if (!mediaType) {
87 return null
88 }
89
90 if (mediaType === 'video') {
91 return <VideoPlayer src={src} className={className} />
92 }
93
94 return <AudioPlayer src={src} className={className} />
95 }
96