feat: add error handling for audio, video, and YouTube players

This commit is contained in:
codytseng
2025-10-09 22:22:16 +08:00
parent 3395bad78b
commit 6eb3bccd38
4 changed files with 38 additions and 3 deletions

View File

@@ -4,6 +4,7 @@ import { cn } from '@/lib/utils'
import mediaManager from '@/services/media-manager.service'
import { Pause, Play } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
import ExternalLink from '../ExternalLink'
interface AudioPlayerProps {
src: string
@@ -15,6 +16,7 @@ export default function AudioPlayer({ src, className }: AudioPlayerProps) {
const [isPlaying, setIsPlaying] = useState(false)
const [currentTime, setCurrentTime] = useState(0)
const [duration, setDuration] = useState(0)
const [error, setError] = useState(false)
const seekTimeoutRef = useRef<NodeJS.Timeout>()
const isSeeking = useRef(false)
@@ -78,6 +80,10 @@ export default function AudioPlayer({ src, className }: AudioPlayerProps) {
}, 300)
}
if (error) {
return <ExternalLink url={src} />
}
return (
<div
className={cn(
@@ -86,7 +92,7 @@ export default function AudioPlayer({ src, className }: AudioPlayerProps) {
)}
onClick={(e) => e.stopPropagation()}
>
<audio ref={audioRef} src={src} preload="metadata" />
<audio ref={audioRef} src={src} preload="metadata" onError={() => setError(false)} />
{/* Play/Pause Button */}
<Button size="icon" className="rounded-full shrink-0" onClick={togglePlay}>

View File

@@ -0,0 +1,15 @@
import { cn } from '@/lib/utils'
export default function ExternalLink({ url, className }: { url: string; className?: string }) {
return (
<a
className={cn('text-primary hover:underline', className)}
href={url}
target="_blank"
onClick={(e) => e.stopPropagation()}
rel="noreferrer"
>
{url}
</a>
)
}

View File

@@ -1,10 +1,12 @@
import { cn, isInViewport } from '@/lib/utils'
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
import mediaManager from '@/services/media-manager.service'
import { useEffect, useRef } from 'react'
import { useEffect, useRef, useState } from 'react'
import ExternalLink from '../ExternalLink'
export default function VideoPlayer({ src, className }: { src: string; className?: string }) {
const { autoplay } = useContentPolicy()
const [error, setError] = useState(false)
const videoRef = useRef<HTMLVideoElement>(null)
const containerRef = useRef<HTMLDivElement>(null)
@@ -38,6 +40,10 @@ export default function VideoPlayer({ src, className }: { src: string; className
}
}, [autoplay])
if (error) {
return <ExternalLink url={src} />
}
return (
<div ref={containerRef}>
<video
@@ -51,6 +57,7 @@ export default function VideoPlayer({ src, className }: { src: string; className
mediaManager.play(event.currentTarget)
}}
muted
onError={() => setError(true)}
/>
</div>
)

View File

@@ -4,6 +4,7 @@ import mediaManager from '@/services/media-manager.service'
import { YouTubePlayer } from '@/types/youtube'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ExternalLink from '../ExternalLink'
export default function YoutubeEmbeddedPlayer({
url,
@@ -19,6 +20,7 @@ export default function YoutubeEmbeddedPlayer({
const [display, setDisplay] = useState(autoLoadMedia)
const { videoId, isShort } = useMemo(() => parseYoutubeUrl(url), [url])
const [initSuccess, setInitSuccess] = useState(false)
const [error, setError] = useState(false)
const playerRef = useRef<YouTubePlayer | null>(null)
const containerRef = useRef<HTMLDivElement>(null)
@@ -63,7 +65,8 @@ export default function YoutubeEmbeddedPlayer({
},
onReady: () => {
setInitSuccess(true)
}
},
onError: () => setError(true)
}
})
} catch (error) {
@@ -79,6 +82,10 @@ export default function YoutubeEmbeddedPlayer({
}
}, [videoId, display, mustLoad])
if (error) {
return <ExternalLink url={url} />
}
if (!mustLoad && !display) {
return (
<div