File size: 17,402 Bytes
9df0966
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
<!DOCTYPE html>
<html lang="fr" class="h-full">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Wami - Demo Live</title>
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="h-full bg-gradient-to-br from-slate-900 via-blue-900 to-slate-900">
    <div class="min-h-full">
        <!-- Header -->
        <header class="bg-slate-800/50 backdrop-blur-sm border-b border-slate-700">
            <div class="max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8">
                <div class="flex items-center justify-between">
                    <div>
                        <h1 class="text-3xl font-bold text-white">🎙️ Wami - Demo Live</h1>
                        <p class="text-slate-300 mt-1">API Dioula STT, TTS & Traduction</p>
                    </div>
                    <a href="/docs" target="_blank" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition">
                        📖 API Docs
                    </a>
                </div>
            </div>
        </header>

        <!-- Main Content -->
        <main class="max-w-7xl mx-auto px-4 py-8 sm:px-6 lg:px-8">
            <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">

                <!-- STT Card -->
                <div class="bg-slate-800/50 backdrop-blur-sm rounded-xl shadow-xl p-6 border border-slate-700">
                    <h2 class="text-2xl font-bold text-white mb-4 flex items-center">
                        <span class="text-3xl mr-2">🎤</span> Speech-to-Text
                    </h2>
                    <p class="text-slate-300 mb-4">Enregistrez votre voix en Dioula</p>

                    <div class="space-y-4">
                        <button id="sttBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-semibold py-4 px-6 rounded-lg transition transform hover:scale-105 flex items-center justify-center gap-2">
                            <span id="sttBtnIcon">🎙️</span>
                            <span id="sttBtnText">Cliquer pour enregistrer</span>
                        </button>

                        <div id="sttStatus" class="text-sm text-slate-400 text-center min-h-6"></div>

                        <div id="sttResult" class="bg-slate-900/50 rounded-lg p-4 min-h-24 text-white hidden">
                            <div class="text-sm text-slate-400 mb-2">Transcription :</div>
                            <div id="sttText" class="text-lg"></div>
                        </div>
                    </div>
                </div>

                <!-- TTS Card -->
                <div class="bg-slate-800/50 backdrop-blur-sm rounded-xl shadow-xl p-6 border border-slate-700">
                    <h2 class="text-2xl font-bold text-white mb-4 flex items-center">
                        <span class="text-3xl mr-2">🔊</span> Text-to-Speech
                    </h2>
                    <p class="text-slate-300 mb-4">Générez un audio en Dioula</p>

                    <div class="space-y-4">
                        <textarea id="ttsInput" class="w-full bg-slate-900/50 text-white rounded-lg p-4 border border-slate-600 focus:border-blue-500 focus:ring-2 focus:ring-blue-500 transition" rows="3" placeholder="Tapez du texte en Dioula..."></textarea>

                        <button id="ttsBtn" class="w-full bg-green-600 hover:bg-green-700 text-white font-semibold py-3 px-6 rounded-lg transition transform hover:scale-105">
                            🔊 Générer la voix
                        </button>

                        <div id="ttsStatus" class="text-sm text-slate-400 text-center min-h-6"></div>

                        <audio id="ttsAudio" controls class="w-full rounded-lg hidden"></audio>
                    </div>
                </div>

                <!-- Translation DYU→FR Card -->
                <div class="bg-slate-800/50 backdrop-blur-sm rounded-xl shadow-xl p-6 border border-slate-700">
                    <h2 class="text-2xl font-bold text-white mb-4 flex items-center">
                        <span class="text-3xl mr-2">🇲🇱</span> Dioula → Français
                    </h2>
                    <p class="text-slate-300 mb-4">Traduisez du Dioula vers le Français</p>

                    <div class="space-y-4">
                        <textarea id="dyuInput" class="w-full bg-slate-900/50 text-white rounded-lg p-4 border border-slate-600 focus:border-purple-500 focus:ring-2 focus:ring-purple-500 transition" rows="3" placeholder="Texte en Dioula..."></textarea>

                        <button id="dyuBtn" class="w-full bg-purple-600 hover:bg-purple-700 text-white font-semibold py-3 px-6 rounded-lg transition transform hover:scale-105">
                            → Traduire en Français
                        </button>

                        <div id="dyuStatus" class="text-sm text-slate-400 text-center min-h-6"></div>

                        <div id="dyuResult" class="bg-slate-900/50 rounded-lg p-4 min-h-20 text-white hidden">
                            <div class="text-sm text-slate-400 mb-2">🇫🇷 Traduction française :</div>
                            <div id="dyuText" class="text-lg"></div>
                        </div>
                    </div>
                </div>

                <!-- Translation FR→DYU Card -->
                <div class="bg-slate-800/50 backdrop-blur-sm rounded-xl shadow-xl p-6 border border-slate-700">
                    <h2 class="text-2xl font-bold text-white mb-4 flex items-center">
                        <span class="text-3xl mr-2">🇫🇷</span> Français → Dioula
                    </h2>
                    <p class="text-slate-300 mb-4">Traduisez du Français vers le Dioula</p>

                    <div class="space-y-4">
                        <textarea id="frInput" class="w-full bg-slate-900/50 text-white rounded-lg p-4 border border-slate-600 focus:border-orange-500 focus:ring-2 focus:ring-orange-500 transition" rows="3" placeholder="Texte en Français..."></textarea>

                        <button id="frBtn" class="w-full bg-orange-600 hover:bg-orange-700 text-white font-semibold py-3 px-6 rounded-lg transition transform hover:scale-105">
                            → Traduire en Dioula
                        </button>

                        <div id="frStatus" class="text-sm text-slate-400 text-center min-h-6"></div>

                        <div id="frResult" class="bg-slate-900/50 rounded-lg p-4 min-h-20 text-white hidden">
                            <div class="text-sm text-slate-400 mb-2">🇲🇱 Traduction dioula :</div>
                            <div id="frText" class="text-lg"></div>
                        </div>
                    </div>
                </div>

            </div>

            <!-- Examples Section -->
            <div class="mt-8 bg-slate-800/50 backdrop-blur-sm rounded-xl shadow-xl p-6 border border-slate-700">
                <h3 class="text-xl font-bold text-white mb-4">💡 Exemples de phrases</h3>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                    <div class="bg-slate-900/50 p-4 rounded-lg">
                        <div class="text-sm text-slate-400 mb-1">Dioula:</div>
                        <div class="text-white">Sanji bɛna kɛ bi</div>
                        <div class="text-sm text-green-400 mt-2">→ Il pleuvra demain</div>
                    </div>
                    <div class="bg-slate-900/50 p-4 rounded-lg">
                        <div class="text-sm text-slate-400 mb-1">Dioula:</div>
                        <div class="text-white">Aw ka séné ninnge ma</div>
                        <div class="text-sm text-green-400 mt-2">→ Protégez vos récoltes</div>
                    </div>
                    <div class="bg-slate-900/50 p-4 rounded-lg">
                        <div class="text-sm text-slate-400 mb-1">Français:</div>
                        <div class="text-white">Bonjour, comment allez-vous?</div>
                        <div class="text-sm text-green-400 mt-2">→ I ni sɔgɔma, i ka kɛnɛya?</div>
                    </div>
                    <div class="bg-slate-900/50 p-4 rounded-lg">
                        <div class="text-sm text-slate-400 mb-1">Français:</div>
                        <div class="text-white">Le temps est beau aujourd'hui</div>
                        <div class="text-sm text-green-400 mt-2">→ Tile ka di bi</div>
                    </div>
                </div>
            </div>
        </main>
    </div>

    <script>
        // ========== STT ==========
        const sttBtn = document.getElementById('sttBtn');
        const sttBtnIcon = document.getElementById('sttBtnIcon');
        const sttBtnText = document.getElementById('sttBtnText');
        const sttStatus = document.getElementById('sttStatus');
        const sttResult = document.getElementById('sttResult');
        const sttText = document.getElementById('sttText');

        let mediaRecorder = null;
        let audioChunks = [];
        let isRecording = false;

        sttBtn.addEventListener('click', async () => {
            if (!isRecording) {
                try {
                    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
                    mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });
                    audioChunks = [];

                    mediaRecorder.ondataavailable = e => {
                        if (e.data.size > 0) audioChunks.push(e.data);
                    };

                    mediaRecorder.onstop = async () => {
                        stream.getTracks().forEach(t => t.stop());
                        const blob = new Blob(audioChunks, { type: 'audio/webm' });
                        await sendAudioToSTT(blob);
                    };

                    mediaRecorder.start();
                    isRecording = true;
                    sttBtn.classList.remove('bg-blue-600', 'hover:bg-blue-700');
                    sttBtn.classList.add('bg-red-600', 'hover:bg-red-700', 'animate-pulse');
                    sttBtnIcon.textContent = '🔴';
                    sttBtnText.textContent = 'Enregistrement... Cliquez pour arrêter';
                } catch (err) {
                    sttStatus.textContent = '❌ Erreur : accès micro refusé';
                    sttStatus.classList.add('text-red-400');
                }
            } else {
                mediaRecorder.stop();
                isRecording = false;
                sttBtn.classList.remove('bg-red-600', 'hover:bg-red-700', 'animate-pulse');
                sttBtn.classList.add('bg-blue-600', 'hover:bg-blue-700');
                sttBtnIcon.textContent = '🎙️';
                sttBtnText.textContent = 'Cliquer pour enregistrer';
            }
        });

        async function sendAudioToSTT(blob) {
            sttStatus.textContent = '⏳ Transcription en cours...';
            sttStatus.classList.remove('text-red-400');
            sttStatus.classList.add('text-blue-400');
            sttResult.classList.add('hidden');

            const formData = new FormData();
            formData.append('audio', blob, 'recording.wav');

            try {
                const res = await fetch('/api/stt', { method: 'POST', body: formData });
                const data = await res.json();

                sttText.textContent = data.transcription || 'Aucun résultat';
                sttResult.classList.remove('hidden');
                sttStatus.textContent = '✅ Transcription terminée';
                sttStatus.classList.remove('text-blue-400');
                sttStatus.classList.add('text-green-400');
            } catch (err) {
                sttStatus.textContent = '❌ Erreur : ' + err.message;
                sttStatus.classList.remove('text-blue-400');
                sttStatus.classList.add('text-red-400');
            }
        }

        // ========== TTS ==========
        const ttsBtn = document.getElementById('ttsBtn');
        const ttsInput = document.getElementById('ttsInput');
        const ttsStatus = document.getElementById('ttsStatus');
        const ttsAudio = document.getElementById('ttsAudio');

        ttsBtn.addEventListener('click', async () => {
            const text = ttsInput.value.trim();
            if (!text) {
                ttsStatus.textContent = '⚠️ Veuillez entrer du texte';
                ttsStatus.classList.add('text-yellow-400');
                return;
            }

            ttsBtn.disabled = true;
            ttsStatus.textContent = '⏳ Génération en cours...';
            ttsStatus.classList.remove('text-yellow-400', 'text-red-400');
            ttsStatus.classList.add('text-blue-400');
            ttsAudio.classList.add('hidden');

            const formData = new FormData();
            formData.append('text', text);

            try {
                const res = await fetch('/api/tts', { method: 'POST', body: formData });
                const blob = await res.blob();
                const url = URL.createObjectURL(blob);

                ttsAudio.src = url;
                ttsAudio.classList.remove('hidden');
                ttsAudio.play();

                ttsStatus.textContent = '✅ Audio généré';
                ttsStatus.classList.remove('text-blue-400');
                ttsStatus.classList.add('text-green-400');
            } catch (err) {
                ttsStatus.textContent = '❌ Erreur : ' + err.message;
                ttsStatus.classList.remove('text-blue-400');
                ttsStatus.classList.add('text-red-400');
            } finally {
                ttsBtn.disabled = false;
            }
        });

        // ========== Translation DYU→FR ==========
        const dyuBtn = document.getElementById('dyuBtn');
        const dyuInput = document.getElementById('dyuInput');
        const dyuStatus = document.getElementById('dyuStatus');
        const dyuResult = document.getElementById('dyuResult');
        const dyuText = document.getElementById('dyuText');

        dyuBtn.addEventListener('click', async () => {
            const text = dyuInput.value.trim();
            if (!text) {
                dyuStatus.textContent = '⚠️ Veuillez entrer du texte';
                dyuStatus.classList.add('text-yellow-400');
                return;
            }

            dyuBtn.disabled = true;
            dyuStatus.textContent = '⏳ Traduction en cours...';
            dyuStatus.classList.remove('text-yellow-400', 'text-red-400');
            dyuStatus.classList.add('text-blue-400');
            dyuResult.classList.add('hidden');

            const formData = new FormData();
            formData.append('text', text);

            try {
                const res = await fetch('/api/translate/dyu-fr', { method: 'POST', body: formData });
                const data = await res.json();

                dyuText.textContent = data.texte_traduit;
                dyuResult.classList.remove('hidden');
                dyuStatus.textContent = '✅ Traduction terminée';
                dyuStatus.classList.remove('text-blue-400');
                dyuStatus.classList.add('text-green-400');
            } catch (err) {
                dyuStatus.textContent = '❌ Erreur : ' + err.message;
                dyuStatus.classList.remove('text-blue-400');
                dyuStatus.classList.add('text-red-400');
            } finally {
                dyuBtn.disabled = false;
            }
        });

        // ========== Translation FR→DYU ==========
        const frBtn = document.getElementById('frBtn');
        const frInput = document.getElementById('frInput');
        const frStatus = document.getElementById('frStatus');
        const frResult = document.getElementById('frResult');
        const frText = document.getElementById('frText');

        frBtn.addEventListener('click', async () => {
            const text = frInput.value.trim();
            if (!text) {
                frStatus.textContent = '⚠️ Veuillez entrer du texte';
                frStatus.classList.add('text-yellow-400');
                return;
            }

            frBtn.disabled = true;
            frStatus.textContent = '⏳ Traduction en cours...';
            frStatus.classList.remove('text-yellow-400', 'text-red-400');
            frStatus.classList.add('text-blue-400');
            frResult.classList.add('hidden');

            const formData = new FormData();
            formData.append('text', text);

            try {
                const res = await fetch('/api/translate/fr-dyu', { method: 'POST', body: formData });
                const data = await res.json();

                frText.textContent = data.texte_traduit;
                frResult.classList.remove('hidden');
                frStatus.textContent = '✅ Traduction terminée';
                frStatus.classList.remove('text-blue-400');
                frStatus.classList.add('text-green-400');
            } catch (err) {
                frStatus.textContent = '❌ Erreur : ' + err.message;
                frStatus.classList.remove('text-blue-400');
                frStatus.classList.add('text-red-400');
            } finally {
                frBtn.disabled = false;
            }
        });
    </script>
</body>
</html>