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