99import threading
1010import math
1111from datetime import datetime , timedelta
12+ import argparse
13+ import logging
14+
15+ # Configurazione Logging
16+ logging .basicConfig (
17+ level = logging .INFO ,
18+ format = '%(asctime)s - %(levelname)s - %(message)s' ,
19+ datefmt = '%Y-%m-%d %H:%M:%S'
20+ )
21+ logger = logging .getLogger (__name__ )
22+
23+ # Lista di stringhe da rimuovere dalla trascrizione
24+ WRONG_SUBSTRINGS = [
25+ "Sottotitoli e revisione a cura di QTSS." ,
26+ "Sottotitoli e revisione a cura di QTSS" ,
27+ "www.mooji.org" ,
28+ "Ondertitels ingediend door de Amara.org gemeenschap" ,
29+ "Ondertiteld door de Amara.org gemeenschap" ,
30+ "Ondertiteling door de Amara.org gemeenschap" ,
31+ "Untertitelung aufgrund der Amara.org-Community" ,
32+ "Untertitel im Auftrag des ZDF für funk, 2017" ,
33+ "Untertitel von Stephanie Geiges" ,
34+ "Untertitel der Amara.org-Community" ,
35+ "Untertitel im Auftrag des ZDF, 2017" ,
36+ "Untertitel im Auftrag des ZDF, 2020" ,
37+ "Untertitel im Auftrag des ZDF, 2018" ,
38+ "Untertitel im Auftrag des ZDF, 2021" ,
39+ "Untertitelung im Auftrag des ZDF, 2021" ,
40+ "Copyright WDR 2021" ,
41+ "Copyright WDR 2020" ,
42+ "Copyright WDR 2019" ,
43+ "SWR 2021" ,
44+ "SWR 2020" ,
45+ "Sous-titres réalisés para la communauté d'Amara.org" ,
46+ "Sous-titres réalisés par la communauté d'Amara.org" ,
47+ "Sous-titres fait par Sous-titres par Amara.org" ,
48+ "Sous-titres réalisés par les SousTitres d'Amara.org" ,
49+ "Sous-titres par Amara.org" ,
50+ "Sous-titres par la communauté d'Amara.org" ,
51+ "Sous-titres réalisés pour la communauté d'Amara.org" ,
52+ "Sous-titres réalisés par la communauté de l'Amara.org" ,
53+ "Sous-Titres faits par la communauté d'Amara.org" ,
54+ "Sous-titres par l'Amara.org" ,
55+ "Sous-titres fait par la communauté d'Amara.org" ,
56+ "Sous-titrage ST' 501" ,
57+ "Sous-titrage ST'501" ,
58+ "Merci d'avoir regardé cette vidéo." ,
59+ "Merci d'avoir regardé cette vidéo!" ,
60+ "Merci d'avoir regardé cette vidéo !" ,
61+ "Merci d'avoir regardé la vidéo." ,
62+ "J'espère que vous avez apprécié la vidéo." ,
63+ "Je vous remercie de vous abonner" ,
64+ "Cliquez-vous sur les sous-titres et abonnez-vous à la chaîne d'Amara.org" ,
65+ "❤️ par SousTitreur.com" ,
66+ "Sottotitoli creati dalla comunità Amara.org" ,
67+ "Sottotitoli di Sottotitoli di Amara.org" ,
68+ "Sottotitoli e revisione al canale di Amara.org" ,
69+ "Sottotitoli e revisione a cura di Amara.org" ,
70+ "Sottotitoli e revisione a cura di QTSS." ,
71+ "Sottotitoli e revisione a cura di QTSS" ,
72+ "Sottotitoli a cura di QTSS" ,
73+ "Sottotitoli creati dalla comunità Amara.org per te." ,
74+ "Subtítulos realizados por la comunidad de Amara.org" ,
75+ "Subtitulado por la comunidad de Amara.org" ,
76+ "Subtítulos por la comunidad de Amara.org" ,
77+ "Subtítulos creados por la comunidad de Amara.org" ,
78+ "Subtítulos en español de Amara.org" ,
79+ "Subtítulos hechos por la comunidad de Amara.org" ,
80+ "Subtitulos por la comunidad de Amara.org" ,
81+ "— Sous-titrage ST'501 —" ,
82+ "Más información www.alimmenta.com" ,
83+ "www.mooji.org" ,
84+ "Subtítulos realizados por la comunidad de Amara.org" ,
85+ "Legendas pela comunidade Amara.org" ,
86+ "Legendas pela comunidade de Amara.org" ,
87+ "Legendas pela comunidade do Amara.org" ,
88+ "Legendas pela comunidade das Amara.org" ,
89+ "Transcrição e Legendas pela comunidade de Amara.org" ,
90+ "Sottotitoli creati dalla comunità Amara.org" ,
91+ "Sous-titres réalisés para la communauté d'Amara.org" ,
92+ "Sous-titres réalisés para la communauté d'Amara.org" ,
93+ "Napisy stworzone przez społeczność Amara.org" ,
94+ "Napisy wykonane przez społeczność Amara.org" ,
95+ "Zdjęcia i napisy stworzone przez społeczność Amara.org" ,
96+ "napisy stworzone przez społeczność Amara.org" ,
97+ "Tłumaczenie i napisy stworzone przez społeczność Amara.org" ,
98+ "Napisy stworzone przez społeczności Amara.org" ,
99+ "Tłumaczenie stworzone przez społeczność Amara.org" ,
100+ "Napisy robione przez społeczność Amara.org" ,
101+ "www.multi-moto.eu" ,
102+ "Редактор субтитров А.Синецкая Корректор А.Егорова" ,
103+ "Yorumlarınızıza abone olmayı unutmayın." ,
104+ "Sottotitoli creati dalla comunità Amara.org" ,"字幕由Amara.org社区提供" ,
105+ "小編字幕由Amara.org社區提供" ,
106+ "[Music]" ,
107+ "[promo]" ,
108+ "[Promo]" ,
109+ "♪" ,
110+ "(upbeat music)" ,
111+ "(Instrumental)" ,
112+ "[BLANK_AUDIO]" ,
113+ "[ cease fire ]" ,
114+ "gu.se" ,
115+ "(majestic music)" ,
116+ "[Pause]" ,
117+ "(snow crunching)" ,
118+ "[Sounds of wind blowing]" ,
119+ "(gulp)" ,
120+ "Sottotitoli e Tsub atki" ,
121+ "[silenzio]" ,
122+ "[LAUGH]" ,
123+ "[ Background noise ]" ,
124+ "[Clapping]" ,
125+ "[SOUND]" ,
126+ "[Sound of metal being hammered against the floor] " ,
127+ "Subtitles by the Amara.org community" ,
128+ "Transcripts by the Amara.org community" ,
129+ "*laughing*" ,
130+ "(laughs)" ,
131+ "[laughs]" ,
132+ "[Laughter]" ,
133+ "*laughter*" ,
134+ "(thud)" ,
135+ "*laughs*" ,
136+ "*lacht*" ,
137+ "[BLANK_AUDIO]" ,
138+ "[Chuckle]" ,
139+ "*Chuckle*" ,
140+ "(laughing)" ,
141+ "Subs by www.zeoranger.co.uk" ,
142+ "*thud*" ,
143+ "*sniff*" ,
144+ "[BLANK_AU" ,
145+ "[BLANK" ,
146+ "[Lacht]" ,
147+ "[Silence]" ,
148+ "[]" ,
149+ "(smacking)" ,
150+ "[Chuckling]" ,
151+ "(air whooshing)" ,
152+ "(whooshing)" ,
153+ "(sighs)" ,
154+ "(blows kiss)" ,
155+ "[Musica]" ,
156+ "[MUSIC PLAYING]" ,
157+ "[BREATHING HEAVILY]" ,
158+ "[Whispering]" ,
159+ "[BEEPING]" ,
160+ "(scratching)" ,
161+ "(wind blowing)" ,
162+ "(swooshing)" ,
163+ "(chicken clucking)" ,
164+ "(footsteps crunching)" ,
165+ "(beeping)" ,
166+ "(birds chirping)" ,
167+ "(sniffing)" ,
168+ "(footsteps)" ,
169+ "Transcribed by https://otter.ai" ,
170+ "[SPEAKING ENGLISH]" ,
171+ "[AUDIO EN BLANCO]" ,
172+ "[AUDIO_EN_BLANCO]" ,
173+ "*Crofie*" ,
174+ "org Subtítulos realizados por la comunidad de Amara." ,
175+ "(Sonido de campanita)" ,
176+ "(Música de suspenso)" ,
177+ "Translation & subtitling by Quentin Dewaghe Traduction &-titrage par Quentin Dewaghe q." ,
178+ "Transcription by ESO;" ,
179+ "translation by —"
180+ ]
181+
182+ def clean_transcription (text ):
183+ """
184+ Rimuove le stringhe indesiderate dalla trascrizione.
185+ """
186+ if not text :
187+ return ""
188+
189+ cleaned_text = text
190+ for wrong_string in WRONG_SUBSTRINGS :
191+ if wrong_string in cleaned_text :
192+ cleaned_text = cleaned_text .replace (wrong_string , "" )
193+
194+ # Rimuove spazi doppi creati dalla rimozione
195+ import re
196+ cleaned_text = re .sub (r'\s+' , ' ' , cleaned_text ).strip ()
197+ return cleaned_text
12198
13199def upgrade_pip_and_install_packages ():
14200 """
@@ -255,7 +441,7 @@ def transcribe_chunk_parallel(chunk_path, model, language='it'):
255441 model: Modello Whisper già caricato
256442 language: Lingua del contenuto
257443 Returns:
258- str: Testo trascritto del chunk
444+ str: Testo trascritto del chunk (pulito)
259445 """
260446 try :
261447 print (f" DEBUG: Inizio trascrizione chunk { os .path .basename (chunk_path )} " )
@@ -282,7 +468,7 @@ def transcribe_chunk_parallel(chunk_path, model, language='it'):
282468 try :
283469 result = model .transcribe (chunk_path , language = language )
284470 print (f" DEBUG: Trascrizione completata per { os .path .basename (chunk_path )} " )
285- return result ['text' ]
471+ return clean_transcription ( result ['text' ])
286472 except (AttributeError , KeyError ) as e :
287473 if "Linear" in str (e ) or any (x in str (e ) for x in ["KeyError" , "transcribe" , "decoder" , "encoder" ]):
288474 print (f" ❌ ERRORE CRITICO: Modello Whisper danneggiato durante la trascrizione" )
@@ -602,7 +788,7 @@ def update_progress():
602788 except :
603789 pass
604790
605- return result ['text' ]
791+ return clean_transcription ( result ['text' ])
606792
607793def save_transcription (transcription , output_path ):
608794 """
@@ -860,44 +1046,74 @@ def main(podcast_dir, model_name='medium', language='it', parallel=False):
8601046 if processed_files > 0 :
8611047 print (f"📈 Tempo medio per file: { total_elapsed / processed_files :.1f} secondi" )
8621048
1049+ def parse_arguments ():
1050+ """
1051+ Analizza gli argomenti da riga di comando.
1052+ """
1053+ parser = argparse .ArgumentParser (description = "Trascrizione automatica file audio con Whisper." )
1054+ parser .add_argument ("--dir" , type = str , help = "Directory contenente i file audio" )
1055+ parser .add_argument ("--model" , type = str , default = "medium" , help = "Modello Whisper da utilizzare (tiny, base, small, medium, large)" )
1056+ parser .add_argument ("--lang" , type = str , default = "it" , help = "Lingua dell'audio (es. it, en)" )
1057+ parser .add_argument ("--parallel" , action = "store_true" , help = "Abilita trascrizione parallela" )
1058+ parser .add_argument ("--no-parallel" , action = "store_false" , dest = "parallel" , help = "Disabilita trascrizione parallela" )
1059+ parser .set_defaults (parallel = False )
1060+
1061+ return parser .parse_args ()
1062+
8631063if __name__ == "__main__" :
8641064 # Verifica che sia utilizzata una versione compatibile di Python
8651065 ensure_python_version ()
8661066
8671067 # Aggiorna pip e installa correttamente whisper e tqdm (solo una volta)
868- upgrade_pip_and_install_packages ()
1068+ try :
1069+ upgrade_pip_and_install_packages ()
1070+ except Exception as e :
1071+ print (f"Errore durante l'aggiornamento dei pacchetti: { e } " )
1072+
1073+ args = parse_arguments ()
8691074
870- while True :
871- podcast_dir = input ("\n Inserisci il percorso della cartella contenente i podcast: " ).strip ()
872-
873- if os .path .isdir (podcast_dir ):
874- print (f"\n Iniziando l'elaborazione della cartella: { podcast_dir } " )
1075+ # Se viene passato un argomento directory, esegui in modalità non interattiva
1076+ if args .dir :
1077+ if os .path .isdir (args .dir ):
1078+ print (f"\n Iniziando l'elaborazione della cartella: { args .dir } " )
1079+ print (f"Modello: { args .model } , Lingua: { args .lang } , Parallelo: { args .parallel } " )
1080+ main (args .dir , model_name = args .model , language = args .lang , parallel = args .parallel )
1081+ else :
1082+ print (f"❌ Errore: La directory { args .dir } non esiste." )
1083+ sys .exit (1 )
1084+ else :
1085+ # Modalità interattiva
1086+ while True :
1087+ podcast_dir = input ("\n Inserisci il percorso della cartella contenente i podcast: " ).strip ()
1088+
1089+ if os .path .isdir (podcast_dir ):
1090+ print (f"\n Iniziando l'elaborazione della cartella: { podcast_dir } " )
1091+
1092+ # Chiedi se utilizzare il processamento parallelo
1093+ while True :
1094+ parallel_choice = input ("Vuoi utilizzare il processamento parallelo per velocizzare la trascrizione? (s/n): " ).strip ().lower ()
1095+ if parallel_choice in ['s' , 'si' , 'yes' , 'y' ]:
1096+ parallel = True
1097+ print ("Modalità processamento parallelo attivata" )
1098+ break
1099+ elif parallel_choice in ['n' , 'no' , 'nope' ]:
1100+ parallel = False
1101+ print ("Modalità normale attivata" )
1102+ break
1103+ else :
1104+ print ("Rispondi 's' per sì o 'n' per no." )
8751105
876- # Chiedi se utilizzare il processamento parallelo
1106+ main (podcast_dir , model_name = 'medium' , language = 'it' , parallel = parallel )
1107+ else :
1108+ print ("Il percorso inserito non è valido. Per favori riprova." )
1109+ continue
1110+
8771111 while True :
878- parallel_choice = input ("Vuoi utilizzare il processamento parallelo per velocizzare la trascrizione? (s/n): " ).strip ().lower ()
879- if parallel_choice in ['s' , 'si' , 'yes' , 'y' ]:
880- parallel = True
881- print ("Modalità processamento parallelo attivata" )
882- break
883- elif parallel_choice in ['n' , 'no' , 'nope' ]:
884- parallel = False
885- print ("Modalità normale attivata" )
1112+ scelta = input ("\n 🔄 Utilizzare di nuovo lo script con una nuova cartella? (1=sì, 0=no): " ).strip ()
1113+ if scelta == '1' :
8861114 break
1115+ elif scelta == '0' :
1116+ print ("👋 Arrivederci!" )
1117+ sys .exit (0 )
8871118 else :
888- print ("Rispondi 's' per sì o 'n' per no." )
889-
890- main (podcast_dir , model_name = 'medium' , language = 'it' , parallel = parallel )
891- else :
892- print ("Il percorso inserito non è valido. Per favore riprova." )
893- continue
894-
895- while True :
896- scelta = input ("\n 🔄 Utilizzare di nuovo lo script con una nuova cartella? (1=sì, 0=no): " ).strip ()
897- if scelta == '1' :
898- break
899- elif scelta == '0' :
900- print ("👋 Arrivederci!" )
901- sys .exit (0 )
902- else :
903- print ("❌ Scelta non valida. Inserire 1 o 0." )
1119+ print ("❌ Scelta non valida. Inserire 1 o 0." )
0 commit comments