00Boobs00 commited on
Commit
90c7fe3
·
verified ·
1 Parent(s): c187f58

Upload components/PocketTTS.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. components/PocketTTS.js +108 -0
components/PocketTTS.js ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useRef } from 'react';
2
+ import { Volume2, Play, Download } from 'lucide-react';
3
+
4
+ export default function PocketTTS() {
5
+ const [text, setText] = useState('Hello, this is a test of PocketTTS integration.');
6
+ const [audioUrl, setAudioUrl] = useState('');
7
+ const [loading, setLoading] = useState(false);
8
+ const audioRef = useRef(null);
9
+
10
+ const handleSynthesize = async () => {
11
+ if (!text.trim()) return;
12
+ setLoading(true);
13
+ setAudioUrl('');
14
+
15
+ try {
16
+ const response = await fetch('/api/tts', {
17
+ method: 'POST',
18
+ headers: { 'Content-Type': 'application/json' },
19
+ body: JSON.stringify({ text }),
20
+ });
21
+
22
+ if (!response.ok) throw new Error('TTS Generation failed');
23
+
24
+ const blob = await response.blob();
25
+ const url = URL.createObjectURL(blob);
26
+ setAudioUrl(url);
27
+ } catch (err) {
28
+ console.error(err);
29
+ alert('Failed to generate audio');
30
+ } finally {
31
+ setLoading(false);
32
+ }
33
+ };
34
+
35
+ const playAudio = () => {
36
+ if (audioRef.current) {
37
+ audioRef.current.play();
38
+ }
39
+ };
40
+
41
+ return (
42
+ <div className="bg-slate-800 rounded-xl p-6 shadow-xl border border-slate-700">
43
+ <div className="flex items-center gap-2 mb-4">
44
+ <Volume2 className="w-5 h-5 text-blue-500" />
45
+ <h2 className="text-lg font-semibold text-white">Pocket TTS</h2>
46
+ </div>
47
+
48
+ <div className="space-y-4">
49
+ <div>
50
+ <label className="block text-sm font-medium text-slate-400 mb-1">
51
+ Text to Speech
52
+ </label>
53
+ <textarea
54
+ value={text}
55
+ onChange={(e) => setText(e.target.value)}
56
+ className="w-full bg-slate-900 border border-slate-600 rounded-lg p-3 text-white placeholder-slate-500 focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all resize-none h-24"
57
+ />
58
+ </div>
59
+
60
+ <div className="flex gap-2">
61
+ <button
62
+ onClick={handleSynthesize}
63
+ disabled={loading}
64
+ className="flex-1 bg-blue-600 hover:bg-blue-500 text-white font-medium py-2 rounded-lg transition-colors disabled:opacity-50 flex items-center justify-center gap-2"
65
+ >
66
+ {loading ? (
67
+ <>
68
+ <div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
69
+ Synthesizing...
70
+ </>
71
+ ) : (
72
+ 'Generate Audio'
73
+ )}
74
+ </button>
75
+
76
+ {audioUrl && (
77
+ <button
78
+ onClick={playAudio}
79
+ className="bg-slate-700 hover:bg-slate-600 text-white p-2 rounded-lg transition-colors"
80
+ title="Play Audio"
81
+ >
82
+ <Play className="w-5 h-5 fill-current" />
83
+ </button>
84
+ )}
85
+ </div>
86
+
87
+ {audioUrl && (
88
+ <div className="bg-slate-900 p-3 rounded-lg border border-slate-700 flex items-center gap-3">
89
+ <audio
90
+ ref={audioRef}
91
+ src={audioUrl}
92
+ controls
93
+ className="w-full h-8"
94
+ />
95
+ <a
96
+ href={audioUrl}
97
+ download="speech.wav"
98
+ className="text-slate-400 hover:text-white transition-colors p-1"
99
+ title="Download Audio"
100
+ >
101
+ <Download className="w-4 h-4" />
102
+ </a>
103
+ </div>
104
+ )}
105
+ </div>
106
+ </div>
107
+ );
108
+ }