ciyidogan commited on
Commit
3586948
Β·
verified Β·
1 Parent(s): 5db49de

Update stt/stt_google.py

Browse files
Files changed (1) hide show
  1. stt/stt_google.py +324 -325
stt/stt_google.py CHANGED
@@ -15,350 +15,349 @@ from utils.logger import log_info, log_error, log_debug, log_warning
15
  from .stt_interface import STTInterface, STTConfig, TranscriptionResult
16
 
17
  class GoogleSTT(STTInterface):
18
- """Google Cloud Speech-to-Text implementation"""
19
- def __init__(self, credentials_path: Optional[str] = None):
20
- """
21
- Initialize Google STT
22
- Args:
23
- credentials_path: Path to service account JSON file (optional if using default credentials)
24
- """
25
- try:
26
- # Initialize client
27
- if credentials_path:
28
- self.client = speech.SpeechClient.from_service_account_file(credentials_path)
29
- log_info(f"βœ… Google STT initialized with service account: {credentials_path}")
30
- else:
31
- # Use default credentials (ADC)
32
- self.client = speech.SpeechClient()
33
- log_info("βœ… Google STT initialized with default credentials")
34
-
35
- # Streaming state
36
- self.is_streaming = False
37
- self.audio_generator = None
38
- self.responses_stream = None
39
- self.audio_queue = queue.Queue()
40
- self.results_queue = queue.Queue(maxsize=100)
41
-
42
- # Session tracking
43
- self.session_id = 0
44
- self.total_audio_bytes = 0
45
- self.total_chunks = 0
46
-
47
- # Threading
48
- self.stream_thread = None
49
- self.stop_event = threading.Event()
50
-
51
- except Exception as e:
52
- log_error(f"❌ Failed to initialize Google STT: {str(e)}")
53
- raise
54
-
55
- def _map_language_code(self, language: str) -> str:
56
- """Map language codes to Google format"""
57
- # Google uses BCP-47 language codes
58
- language_map = {
59
- "tr-TR": "tr-TR",
60
- "en-US": "en-US",
61
- "en-GB": "en-GB",
62
- "de-DE": "de-DE",
63
- "fr-FR": "fr-FR",
64
- "es-ES": "es-ES",
65
- "it-IT": "it-IT",
66
- "pt-BR": "pt-BR",
67
- "ru-RU": "ru-RU",
68
- "ja-JP": "ja-JP",
69
- "ko-KR": "ko-KR",
70
- "zh-CN": "zh-CN",
71
- "ar-SA": "ar-SA",
72
- }
73
- return language_map.get(language, language)
74
-
75
- async def start_streaming(self, config: STTConfig) -> None:
76
- """Initialize streaming session"""
77
- try:
78
- # Stop any existing stream
79
- if self.is_streaming:
80
- log_warning("⚠️ Previous stream still active, stopping it first")
81
- await self.stop_streaming()
82
- await asyncio.sleep(0.5)
83
-
84
- # Reset session data
85
- self._reset_session_data()
86
-
87
- log_info(f"🎀 Starting Google STT - Session #{self.session_id}")
88
-
89
- # Configure recognition settings
90
- language_code = self._map_language_code(config.language)
91
-
92
- # βœ… Google STT best practices for Turkish and single utterance
93
- recognition_config = RecognitionConfig(
94
- encoding=RecognitionConfig.AudioEncoding.LINEAR16,
95
- sample_rate_hertz=16000,
96
- language_code=language_code,
97
- # βœ… Single utterance iΓ§in ideal ayarlar
98
- enable_automatic_punctuation=True,
99
- # Model selection - latest_long for better accuracy
100
- model="latest_long",
101
- # Use enhanced model if available (better for Turkish)
102
- use_enhanced=True,
103
- # Single channel audio
104
- audio_channel_count=1,
105
- # Boost adaptation for better Turkish recognition
106
- speech_contexts=[
107
- speech.SpeechContext(
108
- phrases=[], # Boş bırakıyoruz ama context var
109
- boost=20.0
110
- )
111
- ],
112
- # Alternative transcripts for debugging
113
- max_alternatives=1,
114
- # Profanity filter disabled for accuracy
115
- profanity_filter=False,
116
- # Enable speaker diarization if needed
117
- enable_speaker_diarization=False,
118
- # Word level confidence
119
- enable_word_confidence=False,
120
- enable_spoken_punctuation=False,
121
- enable_spoken_emojis=False,
122
- )
123
-
124
- # βœ… Streaming config - optimized for final results only
125
- self.streaming_config = StreamingRecognitionConfig(
126
- config=recognition_config,
127
- # βœ… Single utterance mode - stops after detecting speech end
128
- single_utterance=True,
129
- # βœ… No interim results - only final
130
- interim_results=False
131
- )
132
-
133
- log_info(f"πŸ”§ Google STT config: language={language_code}, "
134
- f"model=latest_long, enhanced=True, "
135
- f"single_utterance=True, interim_results=False")
136
-
137
- # Start streaming in background thread
138
- self.stop_event.clear()
139
- self.stream_thread = threading.Thread(
140
- target=self._stream_recognition,
141
- daemon=True
142
- )
143
- self.stream_thread.start()
144
-
145
- self.is_streaming = True
146
- log_info(f"βœ… Google STT started - Ready for speech")
147
-
148
- except Exception as e:
149
- log_error(f"❌ Failed to start Google STT", error=str(e))
150
- self.is_streaming = False
151
- raise
152
-
153
- def _stream_recognition(self):
154
- """Background thread for streaming recognition"""
155
- try:
156
- log_debug("πŸŽ™οΈ Starting recognition stream thread")
157
-
158
- # Create audio generator
159
- audio_generator = self._audio_generator()
160
-
161
- # Start streaming recognition
162
- responses = self.client.streaming_recognize(
163
- self.streaming_config,
164
- audio_generator
165
- )
166
-
167
- # Process responses
168
- for response in responses:
169
- if self.stop_event.is_set():
170
- break
171
-
172
- if not response.results:
173
- continue
174
 
175
- # Process each result
176
- for result in response.results:
177
- if not result.alternatives:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  continue
179
 
180
- # Get best alternative
181
- alternative = result.alternatives[0]
182
-
183
- # Only process if we have transcript
184
- if alternative.transcript:
185
- transcription_result = TranscriptionResult(
186
- text=alternative.transcript,
187
- is_final=result.is_final,
188
- confidence=alternative.confidence,
189
- timestamp=datetime.now().timestamp()
190
- )
191
 
192
- try:
193
- self.results_queue.put(transcription_result)
 
 
 
 
 
 
194
 
195
- if result.is_final:
196
- log_info(f"🎯 FINAL TRANSCRIPT: '{alternative.transcript}' "
197
- f"(confidence: {alternative.confidence:.2f})")
198
- # Single utterance mode will end stream after this
199
- break
200
- else:
201
- # This shouldn't happen with interim_results=False
202
- log_debug(f"πŸ“ Transcript: '{alternative.transcript}'")
203
 
204
- except queue.Full:
205
- log_warning("⚠️ Results queue full")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
- # Check if stream ended due to single_utterance
208
- if hasattr(response, 'speech_event_type'):
209
- if response.speech_event_type == speech.StreamingRecognizeResponse.SpeechEventType.END_OF_SINGLE_UTTERANCE:
210
- log_info("πŸ”š End of single utterance detected")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  break
212
 
213
- except Exception as e:
214
- if not self.stop_event.is_set():
215
- log_error(f"❌ Recognition stream error: {str(e)}")
216
- # Put error in queue
217
- error_result = TranscriptionResult(
218
- text="",
219
- is_final=True,
220
- confidence=0.0,
221
- timestamp=datetime.now().timestamp()
222
- )
223
- self.results_queue.put(error_result)
224
- finally:
225
- log_debug("πŸŽ™οΈ Recognition stream thread ended")
226
- self.is_streaming = False
227
-
228
- def _audio_generator(self):
229
- """Generator that yields audio chunks for streaming"""
230
- while not self.stop_event.is_set():
231
  try:
232
- # Get audio chunk with timeout
233
- chunk = self.audio_queue.get(timeout=0.1)
234
 
235
- if chunk is None: # Sentinel value
236
- break
237
-
238
- yield chunk
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
 
240
- except queue.Empty:
241
- continue
242
  except Exception as e:
243
- log_error(f"❌ Audio generator error: {str(e)}")
244
- break
245
-
246
- async def stream_audio(self, audio_chunk: bytes) -> AsyncIterator[TranscriptionResult]:
247
- """Stream audio chunk and get transcription results"""
248
- if not self.is_streaming:
249
- raise RuntimeError("Streaming not started. Call start_streaming() first.")
250
 
251
- try:
252
- # Add audio to queue for background thread
253
- self.audio_queue.put(audio_chunk)
254
-
255
- self.total_chunks += 1
256
- self.total_audio_bytes += len(audio_chunk)
257
-
258
- # Log progress
259
- if self.total_chunks % 50 == 0:
260
- log_debug(f"πŸ“Š Processing... {self.total_chunks} chunks, {self.total_audio_bytes/1024:.1f}KB")
261
-
262
- # Check for results
263
- while True:
264
  try:
265
- result = self.results_queue.get_nowait()
266
-
267
- # Log for debugging
268
- log_debug(f"🎯 Yielding result: is_final={result.is_final}, text='{result.text}'")
269
 
270
- yield result
271
-
272
- # If final result, stream will end
273
- if result.is_final:
274
- self.is_streaming = False
275
-
276
- except queue.Empty:
277
- break
278
-
279
- except Exception as e:
280
- log_error(f"❌ Error streaming audio", error=str(e))
281
- self.is_streaming = False
282
- raise
283
-
284
- async def stop_streaming(self) -> Optional[TranscriptionResult]:
285
- """Stop streaming and clean up"""
286
- if not self.is_streaming:
287
- log_debug("Already stopped, nothing to do")
288
- return None
289
-
290
- try:
291
- log_info(f"πŸ›‘ Stopping Google STT session #{self.session_id}")
292
-
293
- self.is_streaming = False
294
-
295
- # Signal stop
296
- self.stop_event.set()
297
-
298
- # Send sentinel to audio queue
299
- self.audio_queue.put(None)
300
-
301
- # Wait for thread to finish
302
- if self.stream_thread and self.stream_thread.is_alive():
303
- self.stream_thread.join(timeout=2.0)
304
-
305
- # Get final result if any
306
- final_result = None
307
  while not self.results_queue.empty():
308
  try:
309
- result = self.results_queue.get_nowait()
310
- if result.is_final and result.text:
311
- final_result = result
312
- except queue.Empty:
313
- break
314
 
315
- log_info(f"βœ… Google STT session #{self.session_id} stopped")
316
- return final_result
 
 
317
 
318
- except Exception as e:
319
- log_error(f"❌ Error during stop_streaming", error=str(e))
320
- self.is_streaming = False
321
- return None
322
-
323
- def _reset_session_data(self):
324
- """Reset session-specific data"""
325
- # Clear queues
326
- while not self.audio_queue.empty():
327
- try:
328
- self.audio_queue.get_nowait()
329
- except:
330
- pass
331
-
332
- while not self.results_queue.empty():
333
- try:
334
- self.results_queue.get_nowait()
335
- except:
336
- pass
337
 
338
- # Reset counters
339
- self.total_audio_bytes = 0
340
- self.total_chunks = 0
341
- self.session_id += 1
342
 
343
- log_debug(f"πŸ”„ Session data reset. New session ID: {self.session_id}")
344
-
345
- def supports_realtime(self) -> bool:
346
- """Google STT supports real-time streaming"""
347
- return True
348
-
349
- def get_supported_languages(self) -> List[str]:
350
- """Get list of supported language codes"""
351
- # Google Cloud Speech-to-Text supported languages (partial list)
352
- # Full list: https://cloud.google.com/speech-to-text/docs/languages
353
- return [
354
- "tr-TR", "en-US", "en-GB", "en-AU", "en-CA", "en-IN",
355
- "es-ES", "es-MX", "es-AR", "fr-FR", "fr-CA", "de-DE",
356
- "it-IT", "pt-BR", "pt-PT", "ru-RU", "ja-JP", "ko-KR",
357
- "zh-CN", "zh-TW", "ar-SA", "ar-EG", "hi-IN", "nl-NL",
358
- "pl-PL", "sv-SE", "da-DK", "no-NO", "fi-FI", "el-GR",
359
- "he-IL", "th-TH", "vi-VN", "id-ID", "ms-MY", "fil-PH"
360
- ]
361
-
362
- def get_provider_name(self) -> str:
363
- """Get provider name"""
364
- return "google"
 
15
  from .stt_interface import STTInterface, STTConfig, TranscriptionResult
16
 
17
  class GoogleSTT(STTInterface):
18
+ def __init__(self, credentials_path: Optional[str] = None):
19
+ """
20
+ Initialize Google STT
21
+ Args:
22
+ credentials_path: Path to service account JSON file (optional if using default credentials)
23
+ """
24
+ try:
25
+ # Initialize client
26
+ if credentials_path:
27
+ self.client = speech.SpeechClient.from_service_account_file(credentials_path)
28
+ log_info(f"βœ… Google STT initialized with service account: {credentials_path}")
29
+ else:
30
+ # Use default credentials (ADC)
31
+ self.client = speech.SpeechClient()
32
+ log_info("βœ… Google STT initialized with default credentials")
33
+
34
+ # Streaming state
35
+ self.is_streaming = False
36
+ self.audio_generator = None
37
+ self.responses_stream = None
38
+ self.audio_queue = queue.Queue()
39
+ self.results_queue = queue.Queue(maxsize=100)
40
+
41
+ # Session tracking
42
+ self.session_id = 0
43
+ self.total_audio_bytes = 0
44
+ self.total_chunks = 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ # Threading
47
+ self.stream_thread = None
48
+ self.stop_event = threading.Event()
49
+
50
+ except Exception as e:
51
+ log_error(f"❌ Failed to initialize Google STT: {str(e)}")
52
+ raise
53
+
54
+ def _map_language_code(self, language: str) -> str:
55
+ """Map language codes to Google format"""
56
+ # Google uses BCP-47 language codes
57
+ language_map = {
58
+ "tr-TR": "tr-TR",
59
+ "en-US": "en-US",
60
+ "en-GB": "en-GB",
61
+ "de-DE": "de-DE",
62
+ "fr-FR": "fr-FR",
63
+ "es-ES": "es-ES",
64
+ "it-IT": "it-IT",
65
+ "pt-BR": "pt-BR",
66
+ "ru-RU": "ru-RU",
67
+ "ja-JP": "ja-JP",
68
+ "ko-KR": "ko-KR",
69
+ "zh-CN": "zh-CN",
70
+ "ar-SA": "ar-SA",
71
+ }
72
+ return language_map.get(language, language)
73
+
74
+ async def start_streaming(self, config: STTConfig) -> None:
75
+ """Initialize streaming session"""
76
+ try:
77
+ # Stop any existing stream
78
+ if self.is_streaming:
79
+ log_warning("⚠️ Previous stream still active, stopping it first")
80
+ await self.stop_streaming()
81
+ await asyncio.sleep(0.5)
82
+
83
+ # Reset session data
84
+ self._reset_session_data()
85
+
86
+ log_info(f"🎀 Starting Google STT - Session #{self.session_id}")
87
+
88
+ # Configure recognition settings
89
+ language_code = self._map_language_code(config.language)
90
+
91
+ # βœ… Google STT best practices for Turkish and single utterance
92
+ recognition_config = RecognitionConfig(
93
+ encoding=RecognitionConfig.AudioEncoding.LINEAR16,
94
+ sample_rate_hertz=16000,
95
+ language_code=language_code,
96
+ # βœ… Single utterance iΓ§in ideal ayarlar
97
+ enable_automatic_punctuation=True,
98
+ # Model selection - latest_long for better accuracy
99
+ model="latest_long",
100
+ # Use enhanced model if available (better for Turkish)
101
+ use_enhanced=True,
102
+ # Single channel audio
103
+ audio_channel_count=1,
104
+ # Boost adaptation for better Turkish recognition
105
+ speech_contexts=[
106
+ speech.SpeechContext(
107
+ phrases=[], # Boş bırakıyoruz ama context var
108
+ boost=20.0
109
+ )
110
+ ],
111
+ # Alternative transcripts for debugging
112
+ max_alternatives=1,
113
+ # Profanity filter disabled for accuracy
114
+ profanity_filter=False,
115
+ # Enable speaker diarization if needed
116
+ enable_speaker_diarization=False,
117
+ # Word level confidence
118
+ enable_word_confidence=False,
119
+ enable_spoken_punctuation=False,
120
+ enable_spoken_emojis=False,
121
+ )
122
+
123
+ # βœ… Streaming config - optimized for final results only
124
+ self.streaming_config = StreamingRecognitionConfig(
125
+ config=recognition_config,
126
+ # βœ… Single utterance mode - stops after detecting speech end
127
+ single_utterance=True,
128
+ # βœ… No interim results - only final
129
+ interim_results=False
130
+ )
131
+
132
+ log_info(f"πŸ”§ Google STT config: language={language_code}, "
133
+ f"model=latest_long, enhanced=True, "
134
+ f"single_utterance=True, interim_results=False")
135
+
136
+ # Start streaming in background thread
137
+ self.stop_event.clear()
138
+ self.stream_thread = threading.Thread(
139
+ target=self._stream_recognition,
140
+ daemon=True
141
+ )
142
+ self.stream_thread.start()
143
+
144
+ self.is_streaming = True
145
+ log_info(f"βœ… Google STT started - Ready for speech")
146
+
147
+ except Exception as e:
148
+ log_error(f"❌ Failed to start Google STT", error=str(e))
149
+ self.is_streaming = False
150
+ raise
151
+
152
+ def _stream_recognition(self):
153
+ """Background thread for streaming recognition"""
154
+ try:
155
+ log_debug("πŸŽ™οΈ Starting recognition stream thread")
156
+
157
+ # Create audio generator
158
+ audio_generator = self._audio_generator()
159
+
160
+ # Start streaming recognition
161
+ responses = self.client.streaming_recognize(
162
+ self.streaming_config,
163
+ audio_generator
164
+ )
165
+
166
+ # Process responses
167
+ for response in responses:
168
+ if self.stop_event.is_set():
169
+ break
170
+
171
+ if not response.results:
172
  continue
173
 
174
+ # Process each result
175
+ for result in response.results:
176
+ if not result.alternatives:
177
+ continue
178
+
179
+ # Get best alternative
180
+ alternative = result.alternatives[0]
 
 
 
 
181
 
182
+ # Only process if we have transcript
183
+ if alternative.transcript:
184
+ transcription_result = TranscriptionResult(
185
+ text=alternative.transcript,
186
+ is_final=result.is_final,
187
+ confidence=alternative.confidence,
188
+ timestamp=datetime.now().timestamp()
189
+ )
190
 
191
+ try:
192
+ self.results_queue.put(transcription_result)
 
 
 
 
 
 
193
 
194
+ if result.is_final:
195
+ log_info(f"🎯 FINAL TRANSCRIPT: '{alternative.transcript}' "
196
+ f"(confidence: {alternative.confidence:.2f})")
197
+ # Single utterance mode will end stream after this
198
+ break
199
+ else:
200
+ # This shouldn't happen with interim_results=False
201
+ log_debug(f"πŸ“ Transcript: '{alternative.transcript}'")
202
+
203
+ except queue.Full:
204
+ log_warning("⚠️ Results queue full")
205
+
206
+ # Check if stream ended due to single_utterance
207
+ if hasattr(response, 'speech_event_type'):
208
+ if response.speech_event_type == speech.StreamingRecognizeResponse.SpeechEventType.END_OF_SINGLE_UTTERANCE:
209
+ log_info("πŸ”š End of single utterance detected")
210
+ break
211
+
212
+ except Exception as e:
213
+ if not self.stop_event.is_set():
214
+ log_error(f"❌ Recognition stream error: {str(e)}")
215
+ # Put error in queue
216
+ error_result = TranscriptionResult(
217
+ text="",
218
+ is_final=True,
219
+ confidence=0.0,
220
+ timestamp=datetime.now().timestamp()
221
+ )
222
+ self.results_queue.put(error_result)
223
+ finally:
224
+ log_debug("πŸŽ™οΈ Recognition stream thread ended")
225
+ self.is_streaming = False
226
+
227
+ def _audio_generator(self):
228
+ """Generator that yields audio chunks for streaming"""
229
+ while not self.stop_event.is_set():
230
+ try:
231
+ # Get audio chunk with timeout
232
+ chunk = self.audio_queue.get(timeout=0.1)
233
+
234
+ if chunk is None: # Sentinel value
235
+ break
236
+
237
+ yield chunk
238
+
239
+ except queue.Empty:
240
+ continue
241
+ except Exception as e:
242
+ log_error(f"❌ Audio generator error: {str(e)}")
243
+ break
244
+
245
+ async def stream_audio(self, audio_chunk: bytes) -> AsyncIterator[TranscriptionResult]:
246
+ """Stream audio chunk and get transcription results"""
247
+ if not self.is_streaming:
248
+ raise RuntimeError("Streaming not started. Call start_streaming() first.")
249
+
250
+ try:
251
+ # Add audio to queue for background thread
252
+ self.audio_queue.put(audio_chunk)
253
+
254
+ self.total_chunks += 1
255
+ self.total_audio_bytes += len(audio_chunk)
256
 
257
+ # Log progress
258
+ if self.total_chunks % 50 == 0:
259
+ log_debug(f"πŸ“Š Processing... {self.total_chunks} chunks, {self.total_audio_bytes/1024:.1f}KB")
260
+
261
+ # Check for results
262
+ while True:
263
+ try:
264
+ result = self.results_queue.get_nowait()
265
+
266
+ # Log for debugging
267
+ log_debug(f"🎯 Yielding result: is_final={result.is_final}, text='{result.text}'")
268
+
269
+ yield result
270
+
271
+ # If final result, stream will end
272
+ if result.is_final:
273
+ self.is_streaming = False
274
+
275
+ except queue.Empty:
276
  break
277
 
278
+ except Exception as e:
279
+ log_error(f"❌ Error streaming audio", error=str(e))
280
+ self.is_streaming = False
281
+ raise
282
+
283
+ async def stop_streaming(self) -> Optional[TranscriptionResult]:
284
+ """Stop streaming and clean up"""
285
+ if not self.is_streaming:
286
+ log_debug("Already stopped, nothing to do")
287
+ return None
288
+
 
 
 
 
 
 
 
289
  try:
290
+ log_info(f"πŸ›‘ Stopping Google STT session #{self.session_id}")
 
291
 
292
+ self.is_streaming = False
293
+
294
+ # Signal stop
295
+ self.stop_event.set()
296
+
297
+ # Send sentinel to audio queue
298
+ self.audio_queue.put(None)
299
+
300
+ # Wait for thread to finish
301
+ if self.stream_thread and self.stream_thread.is_alive():
302
+ self.stream_thread.join(timeout=2.0)
303
+
304
+ # Get final result if any
305
+ final_result = None
306
+ while not self.results_queue.empty():
307
+ try:
308
+ result = self.results_queue.get_nowait()
309
+ if result.is_final and result.text:
310
+ final_result = result
311
+ except queue.Empty:
312
+ break
313
+
314
+ log_info(f"βœ… Google STT session #{self.session_id} stopped")
315
+ return final_result
316
 
 
 
317
  except Exception as e:
318
+ log_error(f"❌ Error during stop_streaming", error=str(e))
319
+ self.is_streaming = False
320
+ return None
 
 
 
 
321
 
322
+ def _reset_session_data(self):
323
+ """Reset session-specific data"""
324
+ # Clear queues
325
+ while not self.audio_queue.empty():
 
 
 
 
 
 
 
 
 
326
  try:
327
+ self.audio_queue.get_nowait()
328
+ except:
329
+ pass
 
330
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  while not self.results_queue.empty():
332
  try:
333
+ self.results_queue.get_nowait()
334
+ except:
335
+ pass
 
 
336
 
337
+ # Reset counters
338
+ self.total_audio_bytes = 0
339
+ self.total_chunks = 0
340
+ self.session_id += 1
341
 
342
+ log_debug(f"πŸ”„ Session data reset. New session ID: {self.session_id}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
 
344
+ def supports_realtime(self) -> bool:
345
+ """Google STT supports real-time streaming"""
346
+ return True
 
347
 
348
+ def get_supported_languages(self) -> List[str]:
349
+ """Get list of supported language codes"""
350
+ # Google Cloud Speech-to-Text supported languages (partial list)
351
+ # Full list: https://cloud.google.com/speech-to-text/docs/languages
352
+ return [
353
+ "tr-TR", "en-US", "en-GB", "en-AU", "en-CA", "en-IN",
354
+ "es-ES", "es-MX", "es-AR", "fr-FR", "fr-CA", "de-DE",
355
+ "it-IT", "pt-BR", "pt-PT", "ru-RU", "ja-JP", "ko-KR",
356
+ "zh-CN", "zh-TW", "ar-SA", "ar-EG", "hi-IN", "nl-NL",
357
+ "pl-PL", "sv-SE", "da-DK", "no-NO", "fi-FI", "el-GR",
358
+ "he-IL", "th-TH", "vi-VN", "id-ID", "ms-MY", "fil-PH"
359
+ ]
360
+
361
+ def get_provider_name(self) -> str:
362
+ """Get provider name"""
363
+ return "google"