awacke1 commited on
Commit
ffd10f7
·
verified ·
1 Parent(s): 9e4930b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -4
app.py CHANGED
@@ -107,11 +107,12 @@ FILE_EMOJIS = {
107
 
108
  # 5. Voice Recognition Component
109
  def create_voice_component():
110
- """Create auto-starting voice recognition component"""
111
  return components.html(
112
  """
113
  <div style="padding: 20px; border-radius: 10px; background: #f0f2f6;">
114
- <div id="status">Initializing voice recognition...</div>
 
115
  <div id="output" style="margin-top: 10px; padding: 10px; min-height: 100px;
116
  background: white; border-radius: 5px; white-space: pre-wrap;"></div>
117
  <script>
@@ -121,8 +122,11 @@ def create_voice_component():
121
  recognition.interimResults = true;
122
 
123
  const status = document.getElementById('status');
 
124
  const output = document.getElementById('output');
125
  let fullTranscript = '';
 
 
126
 
127
  // Auto-start on load
128
  window.addEventListener('load', () => {
@@ -144,7 +148,8 @@ def create_voice_component():
144
  for (let i = event.resultIndex; i < event.results.length; i++) {
145
  const transcript = event.results[i][0].transcript;
146
  if (event.results[i].isFinal) {
147
- finalTranscript += transcript + '\\n';
 
148
  } else {
149
  interimTranscript += transcript;
150
  }
@@ -152,17 +157,39 @@ def create_voice_component():
152
 
153
  if (finalTranscript) {
154
  fullTranscript += finalTranscript;
 
 
 
 
155
  window.parent.postMessage({
156
  type: 'streamlit:setComponentValue',
157
  value: fullTranscript,
158
  dataType: 'json',
159
  }, '*');
 
 
160
  }
161
 
162
- output.textContent = fullTranscript + (interimTranscript ? '... ' + interimTranscript : '');
163
  output.scrollTop = output.scrollHeight;
164
  };
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  recognition.onend = () => {
167
  try {
168
  recognition.start();
@@ -186,6 +213,104 @@ def create_voice_component():
186
  height=200
187
  )
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  # 6. Audio Processing Functions
190
  def get_autoplay_audio_html(audio_path, width="100%"):
191
  """Create HTML for autoplaying audio with controls"""
 
107
 
108
  # 5. Voice Recognition Component
109
  def create_voice_component():
110
+ """Create auto-searching voice recognition component"""
111
  return components.html(
112
  """
113
  <div style="padding: 20px; border-radius: 10px; background: #f0f2f6;">
114
+ <div id="status" style="margin-bottom: 10px; color: #666;">Starting voice recognition...</div>
115
+ <div id="interim" style="color: #666; min-height: 24px;"></div>
116
  <div id="output" style="margin-top: 10px; padding: 10px; min-height: 100px;
117
  background: white; border-radius: 5px; white-space: pre-wrap;"></div>
118
  <script>
 
122
  recognition.interimResults = true;
123
 
124
  const status = document.getElementById('status');
125
+ const interim = document.getElementById('interim');
126
  const output = document.getElementById('output');
127
  let fullTranscript = '';
128
+ let lastPauseTime = Date.now();
129
+ let pauseThreshold = 1500; // Time in ms to wait before triggering search
130
 
131
  // Auto-start on load
132
  window.addEventListener('load', () => {
 
148
  for (let i = event.resultIndex; i < event.results.length; i++) {
149
  const transcript = event.results[i][0].transcript;
150
  if (event.results[i].isFinal) {
151
+ finalTranscript += transcript + ' ';
152
+ lastPauseTime = Date.now();
153
  } else {
154
  interimTranscript += transcript;
155
  }
 
157
 
158
  if (finalTranscript) {
159
  fullTranscript += finalTranscript;
160
+ interim.textContent = '';
161
+ output.textContent = fullTranscript;
162
+
163
+ // Send to Streamlit for processing
164
  window.parent.postMessage({
165
  type: 'streamlit:setComponentValue',
166
  value: fullTranscript,
167
  dataType: 'json',
168
  }, '*');
169
+ } else if (interimTranscript) {
170
+ interim.textContent = '... ' + interimTranscript;
171
  }
172
 
 
173
  output.scrollTop = output.scrollHeight;
174
  };
175
 
176
+ // Check for pauses and trigger search
177
+ setInterval(() => {
178
+ if (fullTranscript && Date.now() - lastPauseTime > pauseThreshold) {
179
+ if (output.dataset.lastProcessed !== fullTranscript) {
180
+ output.dataset.lastProcessed = fullTranscript;
181
+ window.parent.postMessage({
182
+ type: 'streamlit:setComponentValue',
183
+ value: {
184
+ text: fullTranscript,
185
+ trigger: 'pause'
186
+ },
187
+ dataType: 'json',
188
+ }, '*');
189
+ }
190
+ }
191
+ }, 500);
192
+
193
  recognition.onend = () => {
194
  try {
195
  recognition.start();
 
213
  height=200
214
  )
215
 
216
+ # Available English voices
217
+ ENGLISH_VOICES = [
218
+ "en-US-AriaNeural", # Female, conversational
219
+ "en-US-JennyNeural", # Female, customer service
220
+ "en-US-GuyNeural", # Male, newscast
221
+ "en-US-RogerNeural", # Male, calm
222
+ "en-GB-SoniaNeural", # British female
223
+ "en-GB-RyanNeural", # British male
224
+ "en-AU-NatashaNeural", # Australian female
225
+ "en-AU-WilliamNeural", # Australian male
226
+ "en-CA-ClaraNeural", # Canadian female
227
+ "en-CA-LiamNeural", # Canadian male
228
+ "en-IE-EmilyNeural", # Irish female
229
+ "en-IE-ConnorNeural", # Irish male
230
+ "en-IN-NeerjaNeural", # Indian female
231
+ "en-IN-PrabhatNeural", # Indian male
232
+ ]
233
+
234
+ def render_search_interface():
235
+ """Render main search interface with auto-search voice component"""
236
+ st.header("🔍 Voice Search")
237
+
238
+ # Voice settings
239
+ col1, col2 = st.columns([2, 1])
240
+ with col1:
241
+ selected_voice = st.selectbox(
242
+ "Select Voice",
243
+ ENGLISH_VOICES,
244
+ index=0,
245
+ help="Choose the voice for audio responses"
246
+ )
247
+ with col2:
248
+ auto_search = st.checkbox("Auto-Search on Pause", value=True)
249
+
250
+ # Voice component
251
+ voice_result = create_voice_component()
252
+
253
+ # Handle voice input
254
+ if voice_result and isinstance(voice_result, (str, dict)):
255
+ # Extract text and trigger info
256
+ if isinstance(voice_result, dict):
257
+ current_text = voice_result.get('text', '')
258
+ trigger = voice_result.get('trigger')
259
+ else:
260
+ current_text = voice_result
261
+ trigger = None
262
+
263
+ # Process on pause trigger if enabled
264
+ if auto_search and trigger == 'pause' and current_text:
265
+ if current_text != st.session_state.get('last_processed_text', ''):
266
+ st.session_state.last_processed_text = current_text
267
+
268
+ # Show the detected text
269
+ st.info(f"🎤 Detected: {current_text}")
270
+
271
+ # Perform search
272
+ try:
273
+ with st.spinner("Searching and generating audio response..."):
274
+ response, audio_file = asyncio.run(
275
+ process_voice_search(
276
+ current_text,
277
+ voice=selected_voice
278
+ )
279
+ )
280
+ if response:
281
+ st.markdown(response)
282
+ if audio_file:
283
+ render_audio_result(audio_file, "Search Results")
284
+
285
+ # Save to history
286
+ st.session_state.transcript_history.append({
287
+ 'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
288
+ 'query': current_text,
289
+ 'response': response,
290
+ 'audio': audio_file
291
+ })
292
+ except Exception as e:
293
+ st.error(f"Error processing search: {str(e)}")
294
+
295
+ # Manual search option
296
+ with st.expander("📝 Manual Search", expanded=False):
297
+ query = st.text_input("Search Query:", value=st.session_state.get('last_processed_text', ''))
298
+ if st.button("🔍 Search"):
299
+ try:
300
+ with st.spinner("Searching and generating audio..."):
301
+ response, audio_file = asyncio.run(
302
+ process_voice_search(
303
+ query,
304
+ voice=selected_voice
305
+ )
306
+ )
307
+ if response:
308
+ st.markdown(response)
309
+ if audio_file:
310
+ render_audio_result(audio_file)
311
+ except Exception as e:
312
+ st.error(f"Error processing search: {str(e)}")
313
+
314
  # 6. Audio Processing Functions
315
  def get_autoplay_audio_html(audio_path, width="100%"):
316
  """Create HTML for autoplaying audio with controls"""