import { useRef, useState, useEffect, useCallback, memo } from 'react' import { useTranslation } from 'react-i18next' import AudioPlayerLib from 'react-h5-audio-player' import 'react-h5-audio-player/lib/styles.css' import { Button } from '@/components/ui/button' import { Download } from 'lucide-react' import apiClient from '@/lib/api' import styles from './AudioPlayer.module.css' interface AudioPlayerProps { audioUrl: string jobId: number } const AudioPlayer = memo(({ audioUrl, jobId }: AudioPlayerProps) => { const { t } = useTranslation('common') const [blobUrl, setBlobUrl] = useState('') const [isLoading, setIsLoading] = useState(false) const [loadError, setLoadError] = useState(null) const previousAudioUrlRef = useRef('') useEffect(() => { if (!audioUrl || audioUrl === previousAudioUrlRef.current) return let active = true const prevBlobUrl = blobUrl const fetchAudio = async () => { setIsLoading(true) setLoadError(null) if (prevBlobUrl) { URL.revokeObjectURL(prevBlobUrl) } try { const response = await apiClient.get(audioUrl, { responseType: 'blob' }) if (active) { const url = URL.createObjectURL(response.data) setBlobUrl(url) previousAudioUrlRef.current = audioUrl } } catch (error) { console.error("Failed to load audio:", error) if (active) { setLoadError(t('failedToLoadAudio')) } } finally { if (active) { setIsLoading(false) } } } fetchAudio() return () => { active = false } }, [audioUrl, blobUrl, t]) useEffect(() => { return () => { if (blobUrl) URL.revokeObjectURL(blobUrl) } }, []) const handleDownload = useCallback(() => { const link = document.createElement('a') link.href = blobUrl || audioUrl link.download = `tts-${jobId}-${Date.now()}.wav` link.click() }, [blobUrl, audioUrl, jobId]) if (isLoading) { return (
{t('loadingAudio')}
) } if (loadError) { return (
{loadError}
) } if (!blobUrl) { return null } return (
]} customVolumeControls={[]} showJumpControls={false} volume={1} preload="metadata" autoPlayAfterSrcChange={false} />
) }) AudioPlayer.displayName = 'AudioPlayer' export { AudioPlayer }