22
33import os
44import subprocess
5+ import threading
56import time
67
78import numpy as np
@@ -78,22 +79,37 @@ def _transcribe_from_cmd(self, cmd: list[str], timeout: int = 7200) -> str:
7879 self ._init ()
7980 self ._reset_vad ()
8081 t0 = time .time ()
82+ print (f"[Transcriber] Starting at { time .strftime ('%H:%M:%S' )} " , flush = True )
8183
8284 proc = subprocess .Popen (
83- cmd , stdout = subprocess .PIPE , stderr = subprocess .DEVNULL ,
85+ cmd , stdout = subprocess .PIPE , stderr = subprocess .PIPE ,
8486 )
8587
88+ # Drain stderr in background thread to prevent pipe deadlock
89+ stderr_chunks = []
90+ def _drain_stderr ():
91+ try :
92+ for line in proc .stderr :
93+ stderr_chunks .append (line )
94+ except Exception :
95+ pass
96+ stderr_thread = threading .Thread (target = _drain_stderr , daemon = True )
97+ stderr_thread .start ()
98+
8699 sample_rate = 16000
87100 window_size = 512 # samples per VAD window (32ms at 16kHz)
88101 chunk_size = 16000 # read 1 second at a time from ffmpeg
89102 bytes_per_sample = 4 # float32
90103
91104 texts = []
92105 total_read = 0
106+ total_bytes = 0
107+ last_report = t0
93108
94109 try :
95110 while True :
96- if time .time () - t0 > timeout :
111+ now = time .time ()
112+ if now - t0 > timeout :
97113 proc .kill ()
98114 proc .wait ()
99115 raise TimeoutError (
@@ -104,9 +120,24 @@ def _transcribe_from_cmd(self, cmd: list[str], timeout: int = 7200) -> str:
104120 if not raw :
105121 break
106122
123+ total_bytes += len (raw )
107124 samples = np .frombuffer (raw , dtype = np .float32 )
108125 total_read += len (samples )
109126
127+ # Progress report every 60 seconds
128+ if now - last_report >= 60 :
129+ elapsed_so_far = now - t0
130+ audio_so_far = total_read / sample_rate
131+ speed_kbps = (total_bytes / 1024 ) / elapsed_so_far
132+ print (
133+ f"[Transcriber] Progress: { audio_so_far :.0f} s audio,"
134+ f" { total_bytes / 1024 / 1024 :.1f} MB received,"
135+ f" { speed_kbps :.1f} KB/s,"
136+ f" { len (texts )} segments so far" ,
137+ flush = True ,
138+ )
139+ last_report = now
140+
110141 # Feed samples to VAD in window-sized chunks
111142 idx = 0
112143 while idx + window_size <= len (samples ):
@@ -125,16 +156,35 @@ def _transcribe_from_cmd(self, cmd: list[str], timeout: int = 7200) -> str:
125156 if proc .poll () is None :
126157 proc .kill ()
127158 proc .wait ()
128-
129- if proc .returncode not in (0 , - 9 , None ):
130- raise RuntimeError (f"ffmpeg exited with code { proc .returncode } " )
159+ stderr_thread .join (timeout = 5 )
160+ stderr_output = b"" .join (stderr_chunks )
131161
132162 elapsed = time .time () - t0
133163 duration = total_read / sample_rate
164+
165+ if proc .returncode not in (0 , - 9 , None ):
166+ stderr_text = stderr_output .decode (errors = "replace" )[- 500 :]
167+ raise RuntimeError (
168+ f"ffmpeg exited with code { proc .returncode } .\n "
169+ f"stderr (last 500 chars):\n { stderr_text } "
170+ )
171+
172+ # Warn if no audio received (likely auth/network issue)
173+ if total_bytes == 0 :
174+ stderr_text = stderr_output .decode (errors = "replace" )[- 500 :]
175+ raise RuntimeError (
176+ f"ffmpeg produced no audio output (0 bytes received).\n "
177+ f"stderr (last 500 chars):\n { stderr_text } "
178+ )
179+
180+ speed_kbps = (total_bytes / 1024 ) / elapsed if elapsed > 0 else 0
134181 transcript = " " .join (texts )
135182 print (
136- f"[Transcriber] Done: { duration :.0f} s audio, { len (transcript )} chars,"
137- f" { len (texts )} segments in { elapsed :.0f} s"
183+ f"[Transcriber] Done at { time .strftime ('%H:%M:%S' )} :"
184+ f" { duration :.0f} s audio, { total_bytes / 1024 / 1024 :.1f} MB,"
185+ f" avg { speed_kbps :.1f} KB/s,"
186+ f" { len (transcript )} chars, { len (texts )} segments in { elapsed :.0f} s" ,
187+ flush = True ,
138188 )
139189 return transcript
140190
0 commit comments