@@ -147,42 +147,176 @@ def convert_audio_to_wav(input_path, output_path):
147147 print (f" Errore durante la conversione: { e } " )
148148 return False
149149
150- def speed_up_audio (input_path , output_path , speed_factor = 2.0 ):
150+ def split_audio_into_chunks (input_path , chunk_duration = 300 ):
151151 """
152- Accelera un file audio utilizzando FFmpeg .
152+ Divide un file audio in chunk consecutivi per il processamento parallelo .
153153 Args:
154- input_path: Percorso del file audio originale
155- output_path: Percorso del file audio accelerato
156- speed_factor: Fattore di velocità (2.0 = 2x velocità)
154+ input_path: Percorso del file audio da dividere
155+ chunk_duration: Durata di ogni chunk in secondi (default: 5 minuti)
157156 Returns:
158- bool: True se l'operazione è riuscita, False altrimenti
157+ list: Lista dei percorsi dei chunk creati, o None se fallisce
159158 """
160159 try :
161- # Comando FFmpeg per accelerare l'audio mantenendo il pitch
162- cmd = [
160+ # Crea directory temporanea per i chunk
161+ temp_dir = os .path .dirname (input_path )
162+ base_name = os .path .splitext (os .path .basename (input_path ))[0 ]
163+
164+ # Crea i percorsi per i due chunk
165+ chunk1_path = os .path .join (temp_dir , f"{ base_name } _chunk1.wav" )
166+ chunk2_path = os .path .join (temp_dir , f"{ base_name } _chunk2.wav" )
167+
168+ # Usa FFprobe per ottenere la durata totale
169+ ffprobe_cmd = [
170+ 'ffprobe' , '-v' , 'quiet' , '-show_entries' ,
171+ 'format=duration' , '-of' , 'csv=p=0' , input_path
172+ ]
173+
174+ result = subprocess .run (ffprobe_cmd , capture_output = True , text = True , timeout = 10 )
175+ if result .returncode != 0 :
176+ print (" Impossibile ottenere durata audio" )
177+ return None
178+
179+ total_duration = float (result .stdout .strip ())
180+
181+ # Se l'audio è più corto di chunk_duration * 1.5, elabora come singolo chunk
182+ if total_duration < chunk_duration * 1.5 :
183+ print (f" Audio corto ({ total_duration :.1f} s), elaborazione singola" )
184+ return [input_path ]
185+
186+ # Calcola punto di divisione (metà circa)
187+ split_point = total_duration / 2
188+
189+ print (f" Divisione audio in 2 chunk da ~{ split_point :.1f} s cadauno" )
190+
191+ # Crea primo chunk (da 0 a split_point)
192+ cmd1 = [
163193 'ffmpeg' , '-y' , '-i' , input_path ,
164- '-filter:a' , f'atempo={ speed_factor } ' ,
165- '-vn' , output_path
194+ '-t' , str (split_point ),
195+ '-acodec' , 'pcm_s16le' , '-ar' , '44100' ,
196+ chunk1_path
166197 ]
167198
168- result = subprocess .run (cmd , capture_output = True , text = True , timeout = 60 )
199+ # Crea secondo chunk (da split_point a fine)
200+ cmd2 = [
201+ 'ffmpeg' , '-y' , '-i' , input_path ,
202+ '-ss' , str (split_point ),
203+ '-acodec' , 'pcm_s16le' , '-ar' , '44100' ,
204+ chunk2_path
205+ ]
169206
170- if result .returncode == 0 :
171- print (f" Audio accelerato { speed_factor } x: { os .path .basename (input_path )} " )
172- return True
173- else :
174- print (f" Errore nell'accelerazione audio: { result .stderr } " )
175- return False
207+ # Crea i chunk in sequenza
208+ print (" Creazione chunk 1..." )
209+ result1 = subprocess .run (cmd1 , capture_output = True , text = True , timeout = 60 )
210+
211+ if result1 .returncode != 0 :
212+ print (" Errore creazione chunk 1" )
213+ return None
214+
215+ print (" Creazione chunk 2..." )
216+ result2 = subprocess .run (cmd2 , capture_output = True , text = True , timeout = 60 )
217+
218+ if result2 .returncode != 0 :
219+ print (" Errore creazione chunk 2" )
220+ # Pulisce chunk 1 se chunk 2 fallisce
221+ if os .path .exists (chunk1_path ):
222+ os .remove (chunk1_path )
223+ return None
224+
225+ print (" Chunk creati con successo" )
226+ return [chunk1_path , chunk2_path ]
176227
177- except subprocess .TimeoutExpired :
178- print (" Timeout nell'accelerazione audio" )
179- return False
180- except FileNotFoundError :
181- print (" FFmpeg non trovato. Installa FFmpeg per utilizzare la velocità 2x" )
182- return False
183228 except Exception as e :
184- print (f" Errore durante l'accelerazione: { e } " )
185- return False
229+ print (f" Errore durante la divisione audio: { e } " )
230+ return None
231+
232+ def transcribe_chunk_parallel (chunk_path , model_name = 'medium' , language = 'it' ):
233+ """
234+ Trascrive un singolo chunk audio utilizzando Whisper.
235+ Args:
236+ chunk_path: Percorso del chunk da trascrivere
237+ model_name: Nome del modello Whisper
238+ language: Lingua del contenuto
239+ Returns:
240+ str: Testo trascritto del chunk
241+ """
242+ try :
243+ whisper , tqdm = import_required_modules ()
244+
245+ # Suppress FP16 warning
246+ with warnings .catch_warnings ():
247+ warnings .filterwarnings ("ignore" , message = "FP16 is not supported on CPU; using FP32 instead" )
248+ model = whisper .load_model (model_name )
249+
250+ # Trascrive il chunk
251+ result = model .transcribe (chunk_path , language = language )
252+ return result ['text' ]
253+
254+ except Exception as e :
255+ print (f" Errore nella trascrizione del chunk { os .path .basename (chunk_path )} : { e } " )
256+ return ""
257+
258+ def transcribe_audio_parallel (file_path , model_name = 'medium' , language = 'it' ):
259+ """
260+ Trascrive un file audio dividendo in chunk e processando in parallelo.
261+ Args:
262+ file_path: Percorso del file audio da trascrivere
263+ model_name: Nome del modello Whisper
264+ language: Lingua del contenuto
265+ Returns:
266+ str: Testo trascritto completo
267+ """
268+ import concurrent .futures
269+ import time
270+
271+ print ("Avvio trascrizione parallela..." )
272+
273+ # Dividi l'audio in chunk
274+ chunks = split_audio_into_chunks (file_path )
275+
276+ if not chunks or len (chunks ) == 1 :
277+ # Se non è stato possibile dividere o audio troppo corto, trascrizione singola
278+ print ("Esecuzione trascrizione singola (audio corto o indivisibile)" )
279+ return transcribe_podcast_with_progress (file_path , model_name , language , speed_up = False )
280+
281+ print (f"Trascrizione parallela di { len (chunks )} chunk..." )
282+
283+ start_time = time .time ()
284+
285+ try :
286+ # Avvia trascrizione parallela dei chunk
287+ with concurrent .futures .ThreadPoolExecutor (max_workers = 2 ) as executor :
288+ # Invia i job per i due chunk
289+ future1 = executor .submit (transcribe_chunk_parallel , chunks [0 ], model_name , language )
290+ future2 = executor .submit (transcribe_chunk_parallel , chunks [1 ], model_name , language )
291+
292+ # Attende i risultati
293+ print ("Elaborazione chunk in corso..." )
294+ chunk1_text = future1 .result (timeout = 600 ) # 10 minuti timeout
295+ chunk2_text = future2 .result (timeout = 600 )
296+
297+ # Unisce i risultati
298+ full_transcription = chunk1_text .strip () + " " + chunk2_text .strip ()
299+
300+ elapsed = time .time () - start_time
301+ print (f"Trascrizione parallela completata in { elapsed :.1f} secondi" )
302+
303+ # Pulisce i chunk se sono stati creati
304+ for chunk in chunks :
305+ if chunk != file_path and os .path .exists (chunk ):
306+ try :
307+ os .remove (chunk )
308+ print (f" Chunk { os .path .basename (chunk )} rimosso" )
309+ except Exception as e :
310+ print (f" Attenzione: impossibile rimuovere { chunk } : { e } " )
311+
312+ return full_transcription
313+
314+ except concurrent .futures .TimeoutError :
315+ print ("Timeout nella trascrizione parallela, fallback a trascrizione singola" )
316+ return transcribe_podcast_with_progress (file_path , model_name , language , speed_up = False )
317+ except Exception as e :
318+ print (f"Errore nella trascrizione parallela: { e } , fallback a trascrizione singola" )
319+ return transcribe_podcast_with_progress (file_path , model_name , language , speed_up = False )
186320
187321def get_audio_duration (file_path ):
188322 """
@@ -236,14 +370,14 @@ def get_audio_duration(file_path):
236370 except Exception :
237371 return 300 # Default 5 minuti se tutti i metodi falliscono
238372
239- def transcribe_podcast_with_progress (file_path , model_name = 'medium' , language = 'it' , speed_up = False ):
373+ def transcribe_podcast_with_progress (file_path , model_name = 'medium' , language = 'it' , parallel = False ):
240374 """
241- Trascrive un file audio con barra di progresso basata su chunk temporali .
375+ Trascrive un file audio con barra di progresso e opzionale processamento parallelo .
242376 Args:
243377 file_path: Percorso del file audio da trascrivere
244378 model_name: Nome del modello Whisper da utilizzare
245379 language: Lingua del contenuto audio
246- speed_up : Se True, accelera l'audio 2x prima della trascrizione
380+ parallel : Se True, utilizza processamento parallelo per velocizzare
247381 """
248382 whisper , tqdm = import_required_modules ()
249383
@@ -265,23 +399,11 @@ def transcribe_podcast_with_progress(file_path, model_name='medium', language='i
265399
266400 print ("Trascrizione in corso..." )
267401
268- # Crea file temporaneo se necessario per velocità 2x
269- temp_file_path = None
270- actual_file_path = file_path
271-
272- if speed_up :
273- print ("Accelerazione audio 2x in corso..." )
274- temp_dir = os .path .dirname (file_path )
275- temp_filename = f"temp_speedup_{ os .path .basename (file_path )} "
276- temp_file_path = os .path .join (temp_dir , temp_filename )
277-
278- if speed_up_audio (file_path , temp_file_path , 2.0 ):
279- actual_file_path = temp_file_path
280- print (" Audio accelerato con successo" )
281- else :
282- print (" Impossibile accelerare l'audio, utilizzo file originale" )
283- actual_file_path = file_path
402+ # Utilizza processamento parallelo se richiesto
403+ if parallel :
404+ return transcribe_audio_parallel (file_path , model_name , language )
284405
406+ # Altrimenti, trascrizione singola tradizionale
285407 start_time = time .time ()
286408
287409 # Barra di progresso basata su chunk completati
@@ -327,14 +449,6 @@ def update_progress():
327449 "Durata" : f"{ audio_duration :.1f} s"
328450 })
329451
330- # Pulisce il file temporaneo se è stato creato
331- if temp_file_path and os .path .exists (temp_file_path ):
332- try :
333- os .remove (temp_file_path )
334- print (" File temporaneo rimosso" )
335- except Exception as e :
336- print (f" Attenzione: impossibile rimuovere il file temporaneo: { e } " )
337-
338452 return result ['text' ]
339453
340454def save_transcription (transcription , output_path ):
@@ -367,12 +481,12 @@ def format_time(seconds):
367481 """
368482 return str (timedelta (seconds = int (seconds )))
369483
370- def main (podcast_dir , speed_up = False ):
484+ def main (podcast_dir , parallel = False ):
371485 """
372486 Funzione principale con barra di progresso e ETA.
373487 Args:
374488 podcast_dir: Directory contenente i file audio
375- speed_up : Se True, accelera l'audio 2x prima della trascrizione
489+ parallel : Se True, utilizza processamento parallelo per velocizzare
376490 """
377491 # Importa i moduli necessari
378492 whisper , tqdm = import_required_modules ()
@@ -434,7 +548,7 @@ def main(podcast_dir, speed_up=False):
434548 wav_file_path = file_path
435549
436550 # Procedi con la trascrizione
437- transcription = transcribe_podcast_with_progress (wav_file_path , speed_up = speed_up )
551+ transcription = transcribe_podcast_with_progress (wav_file_path , parallel = parallel )
438552 save_transcription (transcription , output_path )
439553
440554 processed_files += 1
@@ -494,21 +608,21 @@ def main(podcast_dir, speed_up=False):
494608 if os .path .isdir (podcast_dir ):
495609 print (f"\n Iniziando l'elaborazione della cartella: { podcast_dir } " )
496610
497- # Chiedi se utilizzare la velocità 2x
611+ # Chiedi se utilizzare il processamento parallelo
498612 while True :
499- speed_choice = input ("Vuoi accelerare l'audio a 2x velocità per velocizzare la trascrizione? (s/n): " ).strip ().lower ()
500- if speed_choice in ['s' , 'si' , 'yes' , 'y' ]:
501- speed_up = True
502- print ("Modalità velocità 2x attivata" )
613+ parallel_choice = input ("Vuoi utilizzare il processamento parallelo per velocizzare la trascrizione? (s/n): " ).strip ().lower ()
614+ if parallel_choice in ['s' , 'si' , 'yes' , 'y' ]:
615+ parallel = True
616+ print ("Modalità processamento parallelo attivata" )
503617 break
504- elif speed_choice in ['n' , 'no' , 'nope' ]:
505- speed_up = False
618+ elif parallel_choice in ['n' , 'no' , 'nope' ]:
619+ parallel = False
506620 print ("Modalità normale attivata" )
507621 break
508622 else :
509623 print ("Rispondi 's' per sì o 'n' per no." )
510624
511- main (podcast_dir , speed_up = speed_up )
625+ main (podcast_dir , parallel = parallel )
512626 else :
513627 print ("Il percorso inserito non è valido. Per favore riprova." )
514628 continue
0 commit comments