'use client'; import { useState, useRef, useEffect } from 'react'; import { Play, Pause, Volume2, VolumeX, Settings } from 'lucide-react'; export interface AudioPlayerProps { text: string; audioUrl?: string; autoPlay?: boolean; onPlayStart?: () => void; onPlayEnd?: () => void; onError?: (error: string) => void; className?: string; } export interface TTSResponse { audioUrl: string; duration: number; cacheHit: boolean; error?: string; } export default function AudioPlayer({ text, audioUrl: providedAudioUrl, autoPlay = false, onPlayStart, onPlayEnd, onError, className = '' }: AudioPlayerProps) { const [isPlaying, setIsPlaying] = useState(false); const [isLoading, setIsLoading] = useState(false); const [audioUrl, setAudioUrl] = useState(providedAudioUrl || null); const [error, setError] = useState(null); const audioRef = useRef(null); // 生成音頻 const generateAudio = async (textToSpeak: string) => { try { setIsLoading(true); setError(null); const response = await fetch('/api/audio/tts', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token') || ''}` }, body: JSON.stringify({ text: textToSpeak, accent: 'us', speed: 1.0, voice: '' }) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data: TTSResponse = await response.json(); if (data.error) { throw new Error(data.error); } setAudioUrl(data.audioUrl); return data.audioUrl; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Failed to generate audio'; setError(errorMessage); onError?.(errorMessage); return null; } finally { setIsLoading(false); } }; // 播放音頻 const playAudio = async () => { if (!text) { setError('No text to play'); return; } try { let urlToPlay = audioUrl; // 如果沒有音頻 URL,先生成 if (!urlToPlay) { urlToPlay = await generateAudio(text); if (!urlToPlay) return; } const audio = audioRef.current; if (!audio) return; audio.src = urlToPlay; await audio.play(); setIsPlaying(true); onPlayStart?.(); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Failed to play audio'; setError(errorMessage); onError?.(errorMessage); } }; // 暫停音頻 const pauseAudio = () => { const audio = audioRef.current; if (audio) { audio.pause(); setIsPlaying(false); } }; // 切換播放/暫停 const togglePlayPause = (e?: React.MouseEvent) => { e?.stopPropagation(); // 阻止事件冒泡 if (isPlaying) { pauseAudio(); } else { playAudio(); } }; // 處理音頻事件 const handleAudioEnd = () => { setIsPlaying(false); onPlayEnd?.(); }; const handleAudioError = () => { setIsPlaying(false); const errorMessage = 'Audio playback error'; setError(errorMessage); onError?.(errorMessage); }; // 自動播放 useEffect(() => { if (autoPlay && text && !audioUrl) { generateAudio(text); } }, [autoPlay, text]); return (
{/* 隱藏的音頻元素 */}
); }