91 lines
2.2 KiB
TypeScript
91 lines
2.2 KiB
TypeScript
import React, { useState, useRef } from 'react'
|
|
import { Play, Pause, Volume2 } from 'lucide-react'
|
|
|
|
interface AudioPlayerProps {
|
|
text: string
|
|
className?: string
|
|
autoPlay?: boolean
|
|
voice?: 'us' | 'uk'
|
|
speed?: number
|
|
}
|
|
|
|
export default function AudioPlayer({
|
|
text,
|
|
className = '',
|
|
autoPlay = false,
|
|
voice = 'us',
|
|
speed = 1.0
|
|
}: AudioPlayerProps) {
|
|
const [isPlaying, setIsPlaying] = useState(false)
|
|
const [isLoading, setIsLoading] = useState(false)
|
|
const audioRef = useRef<HTMLAudioElement>(null)
|
|
|
|
const handlePlay = async () => {
|
|
if (!text.trim()) return
|
|
|
|
try {
|
|
setIsLoading(true)
|
|
|
|
// 簡單的TTS模擬 - 實際應該調用TTS API
|
|
const utterance = new SpeechSynthesisUtterance(text)
|
|
utterance.lang = voice === 'us' ? 'en-US' : 'en-GB'
|
|
utterance.rate = speed
|
|
|
|
utterance.onstart = () => {
|
|
setIsPlaying(true)
|
|
setIsLoading(false)
|
|
}
|
|
|
|
utterance.onend = () => {
|
|
setIsPlaying(false)
|
|
}
|
|
|
|
utterance.onerror = () => {
|
|
setIsPlaying(false)
|
|
setIsLoading(false)
|
|
}
|
|
|
|
window.speechSynthesis.speak(utterance)
|
|
|
|
} catch (error) {
|
|
console.error('TTS Error:', error)
|
|
setIsLoading(false)
|
|
setIsPlaying(false)
|
|
}
|
|
}
|
|
|
|
const handleStop = () => {
|
|
window.speechSynthesis.cancel()
|
|
setIsPlaying(false)
|
|
}
|
|
|
|
return (
|
|
<button
|
|
onClick={isPlaying ? handleStop : handlePlay}
|
|
disabled={isLoading}
|
|
className={`
|
|
inline-flex items-center gap-2 px-3 py-1.5
|
|
bg-blue-50 hover:bg-blue-100
|
|
border border-blue-200 rounded-lg
|
|
text-blue-700 text-sm font-medium
|
|
transition-colors duration-200
|
|
disabled:opacity-50 disabled:cursor-not-allowed
|
|
${className}
|
|
`}
|
|
>
|
|
{isLoading ? (
|
|
<div className="w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
|
|
) : isPlaying ? (
|
|
<Pause size={16} />
|
|
) : (
|
|
<Play size={16} />
|
|
)}
|
|
|
|
<Volume2 size={14} />
|
|
|
|
<span>
|
|
{isLoading ? '載入中...' : isPlaying ? '播放中' : '播放'}
|
|
</span>
|
|
</button>
|
|
)
|
|
} |