AiDeveloper1 commited on
Commit
1aef0b2
·
verified ·
1 Parent(s): 86c5635

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +330 -75
templates/index.html CHANGED
@@ -2,7 +2,7 @@
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
- <title>Voice Command | Chatbot</title>
6
  <style>
7
  .chat-container {
8
  max-width: 400px;
@@ -33,13 +33,70 @@
33
  border-radius: 5px;
34
  border: 1px solid #ccc;
35
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  </style>
37
  </head>
38
  <body>
 
 
 
 
 
 
 
39
  <div class="chat-container">
40
  <div id="chat-box"></div>
41
-
42
- <!-- Language Selection -->
43
  <select id="languageSelector">
44
  <option value="English (US)">English (US)</option>
45
  <option value="Hindi (India)">Hindi (India)</option>
@@ -48,84 +105,291 @@
48
  <option value="German (Germany)">German (Germany)</option>
49
  <option value="Arabic (Saudi Arabia)">Arabic (Saudi Arabia)</option>
50
  </select>
51
-
52
  <div class="speaker" style="display: flex; justify-content: space-between; width: 100%; box-shadow: 0 0 13px #0000003d; border-radius: 5px; margin-top: 10px;">
53
  <p id="action" style="color: grey; font-weight: 800; padding: 0; padding-left: 2rem;"></p>
54
- <button id="speech" onclick="runSpeechRecog()" style="border: transparent; padding: 0 0.5rem;">
55
  Tap to Speak
56
  </button>
57
  </div>
 
58
  </div>
59
 
60
  <script>
61
- let synth = window.speechSynthesis;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  function runSpeechRecog() {
64
- const selectedLang = document.getElementById("languageSelector").value;
65
  const action = document.getElementById('action');
66
 
67
- // Map language names to speech recognition language codes
68
- const speechLangMap = {
69
- 'English (US)': 'en-US',
70
- 'Hindi (India)': 'hi-IN',
71
- 'Spanish (Spain)': 'es-ES',
72
- 'French (France)': 'fr-FR',
73
- 'German (Germany)': 'de-DE',
74
- 'Arabic (Saudi Arabia)': 'ar-SA'
75
- };
76
 
77
- let recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
78
  recognition.lang = speechLangMap[selectedLang] || 'en-US';
79
  recognition.interimResults = false;
80
  recognition.continuous = false;
81
 
82
  recognition.onstart = () => {
83
- action.innerHTML = "Listening...";
 
84
  };
85
 
86
  recognition.onresult = (event) => {
87
- var transcript = event.results[0][0].transcript;
88
- action.innerHTML = "";
89
  sendMessage(transcript, selectedLang);
90
  };
91
 
92
  recognition.onerror = (event) => {
93
- action.innerHTML = "Error: " + event.error;
 
94
  };
95
 
96
  recognition.onend = () => {
97
- action.innerHTML = "";
98
  };
99
 
100
- recognition.start();
 
 
 
 
 
101
  }
102
 
103
- function sendMessage(message, language) {
 
104
  showUserMessage(message);
105
- sendToFlaskAPI(message, language);
106
- }
107
-
108
- function sendToFlaskAPI(message, language) {
109
- fetch('/api/process_text', {
110
- method: 'POST',
111
- headers: {
112
- 'Content-Type': 'application/json',
113
- },
114
- body: JSON.stringify({ text: message, language: language })
115
- })
116
- .then(response => response.json())
117
- .then(data => {
118
  console.log('Response from Flask API:', data);
119
  handleResponse(data);
120
- })
121
- .catch(error => {
122
  console.error('Error sending data to Flask API:', error);
 
123
  showBotMessage('Error: Unable to process request');
124
- });
125
  }
126
 
 
127
  function handleResponse(data) {
128
  if (data.error) {
 
129
  showBotMessage(data.error);
130
  return;
131
  }
@@ -133,47 +397,38 @@
133
  speakResponse(data.response, data.language);
134
  }
135
 
 
136
  function showUserMessage(message) {
137
- var chatBox = document.getElementById('chat-box');
138
- var userMessageHTML = '<div class="user-message">' + message + '</div>';
139
- chatBox.innerHTML += userMessageHTML;
140
- chatBox.scrollTop = chatBox.scrollHeight; // Auto-scroll to bottom
141
  }
142
 
 
143
  function showBotMessage(message) {
144
- var chatBox = document.getElementById('chat-box');
145
- var botMessageHTML = '<div class="bot-message">' + message + '</div>';
146
- chatBox.innerHTML += botMessageHTML;
147
- chatBox.scrollTop = chatBox.scrollHeight; // Auto-scroll to bottom
148
- }
149
-
150
- function speakResponse(response, language) {
151
- // Map language names to speech synthesis language codes
152
- const speechLangMap = {
153
- 'English (US)': 'en-US',
154
- 'Hindi (India)': 'hi-IN',
155
- 'Spanish (Spain)': 'es-ES',
156
- 'French (France)': 'fr-FR',
157
- 'German (Germany)': 'de-DE',
158
- 'Arabic (Saudi Arabia)': 'ar-SA'
159
- };
160
-
161
- var utterance = new SpeechSynthesisUtterance(response);
162
- utterance.lang = speechLangMap[language] || 'en-US';
163
- synth.speak(utterance);
164
 
165
- // Cancel speech on page unload or new speech input
166
- window.addEventListener('beforeunload', () => {
167
- if (synth.speaking) {
168
- synth.cancel();
169
- }
170
- });
171
- document.getElementById('speech').addEventListener('click', () => {
172
- if (synth.speaking) {
173
- synth.cancel();
174
  }
175
- });
176
- }
 
 
 
 
 
 
 
177
  </script>
178
  </body>
179
  </html>
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <title>Voice Command</title>
6
  <style>
7
  .chat-container {
8
  max-width: 400px;
 
33
  border-radius: 5px;
34
  border: 1px solid #ccc;
35
  }
36
+ #status {
37
+ color: grey;
38
+ font-weight: 600;
39
+ margin-top: 10px;
40
+ text-align: center;
41
+ }
42
+ #permissionModal {
43
+ position: fixed;
44
+ top: 0;
45
+ left: 0;
46
+ width: 100%;
47
+ height: 100%;
48
+ background: rgba(0, 0, 0, 0.5);
49
+ display: flex;
50
+ justify-content: center;
51
+ align-items: center;
52
+ z-index: 1000;
53
+ }
54
+ #permissionModal div {
55
+ background: white;
56
+ padding: 20px;
57
+ border-radius: 5px;
58
+ text-align: center;
59
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
60
+ }
61
+ #permissionModal button {
62
+ margin: 10px;
63
+ padding: 10px 20px;
64
+ border: none;
65
+ border-radius: 5px;
66
+ background: #007bff;
67
+ color: white;
68
+ cursor: pointer;
69
+ }
70
+ #permissionModal button:hover {
71
+ background: #0056b3;
72
+ }
73
+ #testSpeakerButton {
74
+ display: block;
75
+ margin: 10px auto;
76
+ padding: 10px 20px;
77
+ border: none;
78
+ border-radius: 5px;
79
+ background: #28a745;
80
+ color: white;
81
+ cursor: pointer;
82
+ font-family: Arial, sans-serif;
83
+ font-weight: 600;
84
+ }
85
+ #testSpeakerButton:hover {
86
+ background: #218838;
87
+ }
88
  </style>
89
  </head>
90
  <body>
91
+ <div id="permissionModal" style="display: none;">
92
+ <div>
93
+ <p>This site requires microphone and speaker permissions to enable voice input and output.</p>
94
+ <button id="grantPermissions">Grant Permissions</button>
95
+ </div>
96
+ </div>
97
+ <button id="testSpeakerButton">Testing</button>
98
  <div class="chat-container">
99
  <div id="chat-box"></div>
 
 
100
  <select id="languageSelector">
101
  <option value="English (US)">English (US)</option>
102
  <option value="Hindi (India)">Hindi (India)</option>
 
105
  <option value="German (Germany)">German (Germany)</option>
106
  <option value="Arabic (Saudi Arabia)">Arabic (Saudi Arabia)</option>
107
  </select>
 
108
  <div class="speaker" style="display: flex; justify-content: space-between; width: 100%; box-shadow: 0 0 13px #0000003d; border-radius: 5px; margin-top: 10px;">
109
  <p id="action" style="color: grey; font-weight: 800; padding: 0; padding-left: 2rem;"></p>
110
+ <button id="speech" style="border: transparent; padding: 0 0.5rem;">
111
  Tap to Speak
112
  </button>
113
  </div>
114
+ <p id="status"></p>
115
  </div>
116
 
117
  <script>
118
+ // Browser detection
119
+ function detectBrowser() {
120
+ const ua = navigator.userAgent.toLowerCase();
121
+ if (ua.includes('safari') && !ua.includes('chrome')) return 'Safari';
122
+ if (ua.includes('firefox')) return 'Firefox';
123
+ if (ua.includes('edg')) return 'Edge';
124
+ if (ua.includes('chrome')) return 'Chrome';
125
+ return 'Unknown';
126
+ }
127
+
128
+ const browser = detectBrowser();
129
+ const statusBar = document.getElementById('status');
130
+ const permissionModal = document.getElementById('permissionModal');
131
+ const grantPermissionsButton = document.getElementById('grantPermissions');
132
+ const testSpeakerButton = document.getElementById('testSpeakerButton');
133
+
134
+ // Language mapping
135
+ const speechLangMap = {
136
+ 'English (US)': 'en-US',
137
+ 'Hindi (India)': 'hi-IN',
138
+ 'Spanish (Spain)': 'es-ES',
139
+ 'French (France)': 'fr-FR',
140
+ 'German (Germany)': 'de-DE',
141
+ 'Arabic (Saudi Arabia)': 'ar-SA'
142
+ };
143
+
144
+ // Initialize speech synthesis
145
+ const synth = window.speechSynthesis || null;
146
+ let voices = [];
147
+
148
+ // Load voices asynchronously
149
+ function loadVoices() {
150
+ return new Promise((resolve) => {
151
+ if (!synth) {
152
+ statusBar.textContent = 'Text-to-speech not supported in this browser.';
153
+ resolve([]);
154
+ return;
155
+ }
156
+ voices = synth.getVoices();
157
+ if (voices.length > 0) {
158
+ resolve(voices);
159
+ } else {
160
+ synth.addEventListener('voiceschanged', () => {
161
+ voices = synth.getVoices();
162
+ resolve(voices);
163
+ }, { once: true });
164
+ }
165
+ });
166
+ }
167
+
168
+ // Speak text with fallback
169
+ async function speakResponse(text, language) {
170
+ if (!synth) {
171
+ statusBar.textContent = 'Text-to-speech is unavailable. Displaying text only.';
172
+ showBotMessage(text);
173
+ return;
174
+ }
175
+
176
+ const langCode = speechLangMap[language] || 'en-US';
177
+ await loadVoices();
178
+
179
+ const utterance = new SpeechSynthesisUtterance(text);
180
+ let selectedVoice = voices.find(voice => voice.lang === langCode);
181
+ if (!selectedVoice) {
182
+ console.warn(`No voice for ${langCode}. Falling back to English.`);
183
+ selectedVoice = voices.find(voice => voice.lang.startsWith('en')) || voices[0];
184
+ statusBar.textContent = `Voice for ${language} unavailable. Using English voice.`;
185
+ }
186
+
187
+ if (selectedVoice) {
188
+ utterance.voice = selectedVoice;
189
+ utterance.lang = selectedVoice.lang;
190
+ } else {
191
+ statusBar.textContent = 'No voices available for text-to-speech.';
192
+ showBotMessage(text);
193
+ return;
194
+ }
195
+
196
+ utterance.volume = 1.0;
197
+ utterance.rate = 1.0;
198
+ utterance.pitch = 1.0;
199
+ utterance.onerror = (event) => {
200
+ console.error('TTS error:', event.error);
201
+ statusBar.textContent = 'Error in text-to-speech. Displaying text only.';
202
+ showBotMessage(text);
203
+ };
204
+
205
+ utterance.onend = () => {
206
+ console.log('TTS finished.');
207
+ statusBar.textContent = '';
208
+ };
209
+
210
+ if (synth.speaking || synth.paused) {
211
+ synth.cancel();
212
+ }
213
+
214
+ try {
215
+ synth.speak(utterance);
216
+ } catch (error) {
217
+ console.error('TTS failed:', error);
218
+ statusBar.textContent = 'Failed to play speech. Displaying text only.';
219
+ showBotMessage(text);
220
+ }
221
 
222
+ document.getElementById('speech').addEventListener('click', () => {
223
+ if (synth.speaking) synth.cancel();
224
+ }, { once: true });
225
+ }
226
+
227
+ // Initialize TTS and test speaker
228
+ async function testSpeaker() {
229
+ if (!synth) {
230
+ statusBar.textContent = 'Text-to-speech not supported in this browser.';
231
+ return false;
232
+ }
233
+
234
+ try {
235
+ await loadVoices();
236
+ // Silent utterance to unlock audio in Safari
237
+ const silentUtterance = new SpeechSynthesisUtterance('');
238
+ silentUtterance.volume = 0;
239
+ silentUtterance.onend = () => synth.cancel();
240
+ silentUtterance.onerror = (event) => {
241
+ console.error('Silent TTS error:', event.error);
242
+ synth.cancel();
243
+ };
244
+ synth.speak(silentUtterance);
245
+ await new Promise(resolve => setTimeout(resolve, 100));
246
+ synth.cancel();
247
+
248
+ // Test utterance
249
+ const selectedLang = document.getElementById('languageSelector').value;
250
+ const langCode = speechLangMap[selectedLang] || 'en-US';
251
+ const utterance = new SpeechSynthesisUtterance('Speaker works fine');
252
+ let selectedVoice = voices.find(voice => voice.lang === langCode);
253
+ if (!selectedVoice) {
254
+ selectedVoice = voices.find(voice => voice.lang.startsWith('en')) || voices[0];
255
+ statusBar.textContent = `Voice for ${selectedLang} unavailable. Using English voice.`;
256
+ }
257
+
258
+ if (selectedVoice) {
259
+ utterance.voice = selectedVoice;
260
+ utterance.lang = selectedVoice.lang;
261
+ } else {
262
+ statusBar.textContent = 'No voices available for text-to-speech.';
263
+ return false;
264
+ }
265
+
266
+ utterance.volume = 1.0;
267
+ utterance.rate = 1.0;
268
+ utterance.pitch = 1.0;
269
+ utterance.onerror = (event) => {
270
+ console.error('Test TTS error:', event.error);
271
+ statusBar.textContent = 'Error testing speaker.';
272
+ };
273
+ utterance.onend = () => {
274
+ console.log('Test TTS finished.');
275
+ statusBar.textContent = 'Speaker test successful.';
276
+ };
277
+
278
+ synth.speak(utterance);
279
+ return true;
280
+ } catch (error) {
281
+ console.error('TTS test failed:', error);
282
+ statusBar.textContent = 'Failed to test speaker.';
283
+ return false;
284
+ }
285
+ }
286
+
287
+ // Request microphone permission
288
+ async function requestMicPermission() {
289
+ try {
290
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
291
+ stream.getTracks().forEach(track => track.stop());
292
+ return true;
293
+ } catch (error) {
294
+ console.error('Microphone permission error:', error);
295
+ statusBar.textContent = 'Microphone access denied. Voice input unavailable.';
296
+ return false;
297
+ }
298
+ }
299
+
300
+ // Check and request all permissions
301
+ async function checkAndRequestPermissions() {
302
+ if (browser !== 'Safari') return true;
303
+
304
+ try {
305
+ const permissionStatus = await navigator.permissions.query({ name: 'microphone' });
306
+ if (permissionStatus.state === 'granted') {
307
+ return await testSpeaker(); // Test speaker if mic is granted
308
+ }
309
+ } catch (error) {
310
+ console.warn('Permission query not supported:', error);
311
+ }
312
+
313
+ permissionModal.style.display = 'flex';
314
+ return new Promise((resolve) => {
315
+ grantPermissionsButton.onclick = async () => {
316
+ permissionModal.style.display = 'none';
317
+ const micGranted = await requestMicPermission();
318
+ const ttsReady = micGranted ? await testSpeaker() : false;
319
+ if (!micGranted || !ttsReady) {
320
+ statusBar.textContent = 'Some permissions were not granted. Features may be limited.';
321
+ }
322
+ resolve(micGranted && ttsReady);
323
+ };
324
+ });
325
+ }
326
+
327
+ // Speech recognition
328
  function runSpeechRecog() {
329
+ const selectedLang = document.getElementById('languageSelector').value;
330
  const action = document.getElementById('action');
331
 
332
+ if (!window.SpeechRecognition && !window.webkitSpeechRecognition) {
333
+ statusBar.textContent = 'Speech recognition not supported in this browser.';
334
+ return;
335
+ }
 
 
 
 
 
336
 
337
+ const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
338
  recognition.lang = speechLangMap[selectedLang] || 'en-US';
339
  recognition.interimResults = false;
340
  recognition.continuous = false;
341
 
342
  recognition.onstart = () => {
343
+ action.textContent = 'Listening...';
344
+ statusBar.textContent = '';
345
  };
346
 
347
  recognition.onresult = (event) => {
348
+ const transcript = event.results[0][0].transcript;
349
+ action.textContent = '';
350
  sendMessage(transcript, selectedLang);
351
  };
352
 
353
  recognition.onerror = (event) => {
354
+ action.textContent = '';
355
+ statusBar.textContent = `Speech recognition error: ${event.error}`;
356
  };
357
 
358
  recognition.onend = () => {
359
+ action.textContent = '';
360
  };
361
 
362
+ try {
363
+ recognition.start();
364
+ } catch (error) {
365
+ statusBar.textContent = 'Failed to start speech recognition.';
366
+ console.error('STT error:', error);
367
+ }
368
  }
369
 
370
+ // Send message to Flask API
371
+ async function sendMessage(message, language) {
372
  showUserMessage(message);
373
+ try {
374
+ const response = await fetch('/api/process_text', {
375
+ method: 'POST',
376
+ headers: { 'Content-Type': 'application/json' },
377
+ body: JSON.stringify({ text: message, language })
378
+ });
379
+ const data = await response.json();
 
 
 
 
 
 
380
  console.log('Response from Flask API:', data);
381
  handleResponse(data);
382
+ } catch (error) {
 
383
  console.error('Error sending data to Flask API:', error);
384
+ statusBar.textContent = 'Error: Unable to process request';
385
  showBotMessage('Error: Unable to process request');
386
+ }
387
  }
388
 
389
+ // Handle API response
390
  function handleResponse(data) {
391
  if (data.error) {
392
+ statusBar.textContent = data.error;
393
  showBotMessage(data.error);
394
  return;
395
  }
 
397
  speakResponse(data.response, data.language);
398
  }
399
 
400
+ // Show user message
401
  function showUserMessage(message) {
402
+ const chatBox = document.getElementById('chat-box');
403
+ chatBox.innerHTML += `<div class="user-message">${message}</div>`;
404
+ chatBox.scrollTop = chatBox.scrollHeight;
 
405
  }
406
 
407
+ // Show bot message
408
  function showBotMessage(message) {
409
+ const chatBox = document.getElementById('chat-box');
410
+ chatBox.innerHTML += `<div class="bot-message">${message}</div>`;
411
+ chatBox.scrollTop = chatBox.scrollHeight;
412
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
 
414
+ // Initialize
415
+ window.addEventListener('load', async () => {
416
+ await loadVoices();
417
+ if (browser === 'Safari') {
418
+ statusBar.textContent = 'Safari detected. Please grant microphone and speaker permissions.';
419
+ const permissionsGranted = await checkAndRequestPermissions();
420
+ if (!permissionsGranted) {
421
+ statusBar.textContent = 'Permissions denied. Some features may not work.';
 
422
  }
423
+ }
424
+ document.getElementById('speech').addEventListener('click', runSpeechRecog);
425
+ testSpeakerButton.addEventListener('click', testSpeaker);
426
+ });
427
+
428
+ // Clean up on unload
429
+ window.addEventListener('beforeunload', () => {
430
+ if (synth && synth.speaking) synth.cancel();
431
+ });
432
  </script>
433
  </body>
434
  </html>