77from pathlib import Path
88from typing import Optional , Tuple , List , Set
99from uuid import UUID
10+ from dataclasses import dataclass
1011
1112# Fix SSL certificate verification for bundled applications (macOS, Windows)
1213# This must be done before importing demucs which uses torch.hub with urllib
@@ -97,11 +98,15 @@ def callback(progress):
9798from buzz .transcriber .whisper_file_transcriber import WhisperFileTranscriber
9899
99100
101+ @dataclass
102+ class _CurrentTranscription :
103+ task : Optional [FileTranscriptionTask ] = None
104+ transcriber : Optional [FileTranscriber ] = None
105+ transcriber_thread : Optional [QThread ] = None
106+
107+
100108class FileTranscriberQueueWorker (QObject ):
101109 tasks_queue : multiprocessing .Queue
102- current_task : Optional [FileTranscriptionTask ] = None
103- current_transcriber : Optional [FileTranscriber ] = None
104- current_transcriber_thread : Optional [QThread ] = None
105110
106111 task_started = pyqtSignal (FileTranscriptionTask )
107112 task_progress = pyqtSignal (FileTranscriptionTask , float )
@@ -115,8 +120,8 @@ class FileTranscriberQueueWorker(QObject):
115120 def __init__ (self , parent : Optional [QObject ] = None ):
116121 super ().__init__ (parent )
117122 self .tasks_queue = queue .Queue ()
123+ self .current = _CurrentTranscription ()
118124 self .canceled_tasks : Set [UUID ] = set ()
119- self .current_transcriber = None
120125 self .speech_path = None
121126 self .speech_extractor_process = None
122127 self .is_running = False
@@ -143,7 +148,7 @@ def run(self):
143148
144149 self .is_running = True
145150
146- if self .current_task .transcription_options .extract_speech :
151+ if self .current . task .transcription_options .extract_speech :
147152 status = self ._setup_speech_extraction ()
148153 if status == "error" :
149154 self .is_running = False
@@ -153,22 +158,22 @@ def run(self):
153158 return
154159
155160 logging .debug ("Starting next transcription task" )
156- self .task_progress .emit (self .current_task , 0 )
161+ self .task_progress .emit (self .current . task , 0 )
157162
158163 self ._create_transcriber ()
159164 self ._setup_transcriber_thread ()
160165
161166 def _cleanup_previous_transcriber (self ):
162- if self .current_transcriber is not None :
163- self .current_transcriber .stop ()
164- self .current_transcriber = None
167+ if self .current . transcriber is not None :
168+ self .current . transcriber .stop ()
169+ self .current . transcriber = None
165170
166171 def _get_next_task (self ) -> bool :
167172 while True :
168- self .current_task = self .tasks_queue .get ()
169- if self .current_task is None :
173+ self .current . task = self .tasks_queue .get ()
174+ if self .current . task is None :
170175 return False
171- if self .current_task .uid in self .canceled_tasks :
176+ if self .current . task .uid in self .canceled_tasks :
172177 continue
173178 return True
174179
@@ -182,21 +187,21 @@ def _setup_speech_extraction(self) -> str:
182187 import torch
183188 device = "cuda" if torch .cuda .is_available () else "cpu"
184189
185- task_file_path = Path (self .current_task .file_path )
190+ task_file_path = Path (self .current . task .file_path )
186191 speech_path = task_file_path .with_name (f"{ task_file_path .stem } _speech.mp3" )
187192
188193 status = self ._extract_speech (str (task_file_path ), str (speech_path ), device )
189194
190195 if status == "error" :
191196 self .task_error .emit (
192- self .current_task ,
197+ self .current . task ,
193198 _ ("Speech extraction failed! Check your internet connection \u2014 a model may need to be downloaded." ),
194199 )
195200 elif status == "ok" :
196201 self .speech_path = speech_path
197- if not self .current_task .original_file_path :
198- self .current_task .original_file_path = str (task_file_path )
199- self .current_task .file_path = str (speech_path )
202+ if not self .current . task .original_file_path :
203+ self .current . task .original_file_path = str (task_file_path )
204+ self .current . task .file_path = str (speech_path )
200205
201206 return status
202207
@@ -208,14 +213,14 @@ def _run_plugins(self) -> bool:
208213 """
209214 if self .plugin_manager is not None :
210215 try :
211- self .plugin_manager .run_before_transcription (self .current_task )
216+ self .plugin_manager .run_before_transcription (self .current . task )
212217 except Exception as e :
213218 logging .error (f"Plugin before_transcription failed: { e } " , exc_info = True )
214219
215- should_skip , skip_segments = self .plugin_manager .run_check_skip (self .current_task )
220+ should_skip , skip_segments = self .plugin_manager .run_check_skip (self .current . task )
216221 if should_skip :
217222 logging .debug ("Skipping transcription task (plugin signaled skip)" )
218- self .current_task .status = FileTranscriptionTask .Status .SKIPPED
223+ self .current . task .status = FileTranscriptionTask .Status .SKIPPED
219224 self .on_task_completed (skip_segments )
220225 self .is_running = False
221226 self ._on_task_finished ()
@@ -224,49 +229,49 @@ def _run_plugins(self) -> bool:
224229 return True
225230
226231 def _create_transcriber (self ):
227- model_type = self .current_task .transcription_options .model .model_type
232+ model_type = self .current . task .transcription_options .model .model_type
228233 if model_type == ModelType .OPEN_AI_WHISPER_API :
229- self .current_transcriber = OpenAIWhisperAPIFileTranscriber (
230- task = self .current_task
234+ self .current . transcriber = OpenAIWhisperAPIFileTranscriber (
235+ task = self .current . task
231236 )
232237 elif (
233238 model_type == ModelType .WHISPER_CPP
234239 or model_type == ModelType .HUGGING_FACE
235240 or model_type == ModelType .WHISPER
236241 or model_type == ModelType .FASTER_WHISPER
237242 ):
238- self .current_transcriber = WhisperFileTranscriber (task = self .current_task )
243+ self .current . transcriber = WhisperFileTranscriber (task = self .current . task )
239244 else :
240245 raise Exception (f"Unknown model type: { model_type } " )
241246
242247 def _setup_transcriber_thread (self ):
243- self .current_transcriber_thread = QThread (self )
248+ self .current . transcriber_thread = QThread (self )
244249
245- self .current_transcriber . moveToThread (self .current_transcriber_thread )
250+ self .current . transcriber . moveToThread (self .current . transcriber_thread )
246251
247- self .current_transcriber_thread . started .connect (self .current_transcriber .run )
248- self .current_transcriber . completed .connect (self .current_transcriber_thread .quit )
249- self .current_transcriber . error .connect (self .current_transcriber_thread .quit )
252+ self .current . transcriber_thread . started .connect (self .current . transcriber .run )
253+ self .current . transcriber . completed .connect (self .current . transcriber_thread .quit )
254+ self .current . transcriber . error .connect (self .current . transcriber_thread .quit )
250255
251- self .current_transcriber . completed .connect (self .current_transcriber .deleteLater )
252- self .current_transcriber . error .connect (self .current_transcriber .deleteLater )
253- self .current_transcriber_thread .finished .connect (
254- self .current_transcriber_thread .deleteLater
256+ self .current . transcriber . completed .connect (self .current . transcriber .deleteLater )
257+ self .current . transcriber . error .connect (self .current . transcriber .deleteLater )
258+ self .current . transcriber_thread .finished .connect (
259+ self .current . transcriber_thread .deleteLater
255260 )
256261
257- self .current_transcriber .progress .connect (self .on_task_progress )
258- self .current_transcriber .download_progress .connect (
262+ self .current . transcriber .progress .connect (self .on_task_progress )
263+ self .current . transcriber .download_progress .connect (
259264 self .on_task_download_progress
260265 )
261- self .current_transcriber .error .connect (self .on_task_error )
266+ self .current . transcriber .error .connect (self .on_task_error )
262267
263- self .current_transcriber .completed .connect (self .on_task_completed )
268+ self .current . transcriber .completed .connect (self .on_task_completed )
264269
265- self .current_transcriber .error .connect (lambda : self ._on_task_finished ())
266- self .current_transcriber .completed .connect (lambda : self ._on_task_finished ())
270+ self .current . transcriber .error .connect (lambda : self ._on_task_finished ())
271+ self .current . transcriber .completed .connect (lambda : self ._on_task_finished ())
267272
268- self .task_started .emit (self .current_task )
269- self .current_transcriber_thread .start ()
273+ self .task_started .emit (self .current . task )
274+ self .current . transcriber_thread .start ()
270275
271276 def _extract_speech (self , file_path : str , speech_path : str , device : str ) -> str :
272277 """Run demucs speech extraction in a separate process.
@@ -298,7 +303,7 @@ def _extract_speech(self, file_path: str, speech_path: str, device: str) -> str:
298303 audio_length = int (message [2 ] * 100 )
299304 if audio_length :
300305 self .task_progress .emit (
301- self .current_task ,
306+ self .current . task ,
302307 int (message [1 ] * 100 ) / audio_length ,
303308 )
304309 elif kind == "done" :
@@ -364,44 +369,44 @@ def add_task(self, task: FileTranscriptionTask):
364369 def cancel_task (self , task_id : UUID ):
365370 self .canceled_tasks .add (task_id )
366371
367- if self .current_task is not None and self .current_task .uid == task_id :
372+ if self .current . task is not None and self .current . task .uid == task_id :
368373 self ._terminate_speech_extractor_process ()
369374
370- if self .current_transcriber is not None :
371- self .current_transcriber .stop ()
375+ if self .current . transcriber is not None :
376+ self .current . transcriber .stop ()
372377
373- if self .current_transcriber_thread is not None :
374- if not self .current_transcriber_thread .wait (5000 ):
378+ if self .current . transcriber_thread is not None :
379+ if not self .current . transcriber_thread .wait (5000 ):
375380 logging .warning ("Transcriber thread did not terminate gracefully" )
376- self .current_transcriber_thread .terminate ()
381+ self .current . transcriber_thread .terminate ()
377382
378383 def on_task_error (self , error : str ):
379384 if (
380- self .current_task is not None
381- and self .current_task .uid not in self .canceled_tasks
385+ self .current . task is not None
386+ and self .current . task .uid not in self .canceled_tasks
382387 ):
383388 # Check if the error indicates cancellation
384389 if "canceled" in error .lower () or "cancelled" in error .lower ():
385- self .current_task .status = FileTranscriptionTask .Status .CANCELED
386- self .current_task .error = error
390+ self .current . task .status = FileTranscriptionTask .Status .CANCELED
391+ self .current . task .error = error
387392 else :
388- self .current_task .status = FileTranscriptionTask .Status .FAILED
389- self .current_task .error = error
390- self .task_error .emit (self .current_task , error )
393+ self .current . task .status = FileTranscriptionTask .Status .FAILED
394+ self .current . task .error = error
395+ self .task_error .emit (self .current . task , error )
391396
392397 @pyqtSlot (tuple )
393398 def on_task_progress (self , progress : Tuple [int , int ]):
394- if self .current_task is not None :
395- self .task_progress .emit (self .current_task , progress [0 ] / progress [1 ])
399+ if self .current . task is not None :
400+ self .task_progress .emit (self .current . task , progress [0 ] / progress [1 ])
396401
397402 def on_task_download_progress (self , fraction_downloaded : float ):
398- if self .current_task is not None :
399- self .task_download_progress .emit (self .current_task , fraction_downloaded )
403+ if self .current . task is not None :
404+ self .task_download_progress .emit (self .current . task , fraction_downloaded )
400405
401406 @pyqtSlot (list )
402407 def on_task_completed (self , segments : List [Segment ]):
403- if self .current_task is not None :
404- self .task_completed .emit (self .current_task , segments )
408+ if self .current . task is not None :
409+ self .task_completed .emit (self .current . task , segments )
405410
406411 if self .speech_path is not None :
407412 try :
@@ -412,8 +417,8 @@ def on_task_completed(self, segments: List[Segment]):
412417
413418 def stop (self ):
414419 self .tasks_queue .put (None )
415- if self .current_transcriber is not None :
416- self .current_transcriber .stop ()
420+ if self .current . transcriber is not None :
421+ self .current . transcriber .stop ()
417422
418423 # Terminate the speech extraction process if one is still running.
419424 self ._terminate_speech_extractor_process ()
0 commit comments