binaryMao commited on
Commit
4d8b436
·
verified ·
1 Parent(s): a364c86

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +16 -19
app.py CHANGED
@@ -9,9 +9,9 @@ from huggingface_hub import snapshot_download
9
  from nemo.collections import asr as nemo_asr
10
  import gradio as gr
11
 
12
- # 1. CONFIGURATION ET MODÈLES (LISTE COMPLÈTE)
13
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
14
- SEGMENT_DURATION = 5.0 # Ta préférence stricte pour Soloni
15
 
16
  MODELS = {
17
  "Soloba V3 (CTC)": ("RobotsMali/soloba-ctc-0.6b-v3", "ctc"),
@@ -38,14 +38,13 @@ def find_example_video():
38
  try:
39
  subprocess.run(f"wget {example_url} -O {target_path}", shell=True, check=True)
40
  return target_path
41
- except Exception as e:
42
- print(f"⚠️ Impossible de télécharger l'exemple : {e}")
43
  return None
44
 
45
  EXAMPLE_PATH = find_example_video()
46
  _cache = {}
47
 
48
- # 2. GESTION MÉMOIRE ET CHARGEMENT SÉCURISÉ (CORRECTIF BUG)
49
  def clear_memory():
50
  _cache.clear()
51
  gc.collect()
@@ -61,17 +60,17 @@ def get_model(name):
61
  nemo_file = next((os.path.join(folder, f) for f in os.listdir(folder) if f.endswith(".nemo")), None)
62
  if not nemo_file: raise FileNotFoundError("Fichier .nemo introuvable.")
63
 
64
- # CORRECTIF : On instancie le connecteur explicitement pour éviter l'erreur object.init()
65
  from nemo.core.connectors.save_restore_connector import SaveRestoreConnector
66
- connector = SaveRestoreConnector()
67
-
68
  model = nemo_asr.models.ASRModel.restore_from(
69
  nemo_file,
70
  map_location=torch.device(DEVICE),
71
- save_restore_connector=connector
72
  )
73
 
74
- model.to(DEVICE).eval()
75
  if DEVICE == "cuda":
76
  model = model.half()
77
 
@@ -92,22 +91,21 @@ def pipeline(video_in, model_name):
92
  yield "❌ Aucune vidéo sélectionnée.", None
93
  return
94
 
95
- yield "⏳ Phase 1/4 : Extraction audio...", None
96
  full_wav = os.path.join(tmp_dir, "full.wav")
97
  subprocess.run(f"ffmpeg -y -i {shlex.quote(video_in)} -vn -ac 1 -ar 16000 {full_wav}", shell=True, check=True)
98
 
99
- yield f"⏳ Phase 2/4 : Segmentation ({SEGMENT_DURATION}s)...", None
100
  subprocess.run(f"ffmpeg -i {full_wav} -f segment -segment_time {SEGMENT_DURATION} -c copy {os.path.join(tmp_dir, 'seg_%03d.wav')}", shell=True, check=True)
101
 
102
  files = sorted(glob.glob(os.path.join(tmp_dir, "seg_*.wav")))
103
- # Sécurité : Ignorer les fichiers vides (<1.5 Ko)
104
  valid_segments = [f for f in files if os.path.getsize(f) > 1500]
105
 
106
  if not valid_segments:
107
- yield "❌ Erreur : Audio invalide.", None
108
  return
109
 
110
- yield f"⏳ Phase 3/4 : Chargement de {model_name}...", None
111
  model = get_model(model_name)
112
 
113
  yield f"🎙️ Transcription de {len(valid_segments)} segments...", None
@@ -131,7 +129,7 @@ def pipeline(video_in, model_name):
131
  "end": base_time + ((i+1) * gap)
132
  })
133
 
134
- yield "⏳ Phase 4/4 : Encodage vidéo...", None
135
  srt_path = os.path.join(tmp_dir, "final.srt")
136
  with open(srt_path, "w", encoding="utf-8") as f:
137
  for i in range(0, len(all_words_ts), 6):
@@ -142,7 +140,7 @@ def pipeline(video_in, model_name):
142
  out_path = os.path.abspath(f"resultat_{int(time.time())}.mp4")
143
  safe_srt = srt_path.replace("\\", "/").replace(":", "\\:")
144
 
145
- cmd = f"ffmpeg -y -i {shlex.quote(video_in)} -vf \"subtitles='{safe_srt}':force_style='Alignment=2,FontSize=18,PrimaryColour=&H00FFFF'\" -c:v libx264 -preset ultrafast -c:a copy {out_path}"
146
  subprocess.run(cmd, shell=True, check=True)
147
 
148
  yield "✅ Terminé !", out_path
@@ -163,13 +161,12 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
163
  m_input = gr.Dropdown(choices=list(MODELS.keys()), value="Soloni V3 (TDT-CTC)", label="Modèle")
164
  run_btn = gr.Button("🚀 GÉNÉRER", variant="primary")
165
 
166
- # Rétabli : Affichage des exemples si trouvés
167
  if EXAMPLE_PATH:
168
  gr.Examples(examples=[[EXAMPLE_PATH, "Soloni V3 (TDT-CTC)"]], inputs=[v_input, m_input])
169
 
170
  with gr.Column():
171
  status = gr.Markdown("### État\nPrêt.")
172
- v_output = gr.Video(label="Vidéo finale")
173
 
174
  run_btn.click(pipeline, [v_input, m_input], [status, v_output])
175
 
 
9
  from nemo.collections import asr as nemo_asr
10
  import gradio as gr
11
 
12
+ # 1. CONFIGURATION ET MODÈLES (INTÉGRALITÉ CONSERVÉE)
13
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
14
+ SEGMENT_DURATION = 5.0
15
 
16
  MODELS = {
17
  "Soloba V3 (CTC)": ("RobotsMali/soloba-ctc-0.6b-v3", "ctc"),
 
38
  try:
39
  subprocess.run(f"wget {example_url} -O {target_path}", shell=True, check=True)
40
  return target_path
41
+ except Exception:
 
42
  return None
43
 
44
  EXAMPLE_PATH = find_example_video()
45
  _cache = {}
46
 
47
+ # 2. GESTION MÉMOIRE ET CHARGEMENT SÉCURISÉ
48
  def clear_memory():
49
  _cache.clear()
50
  gc.collect()
 
60
  nemo_file = next((os.path.join(folder, f) for f in os.listdir(folder) if f.endswith(".nemo")), None)
61
  if not nemo_file: raise FileNotFoundError("Fichier .nemo introuvable.")
62
 
63
+ # FIX CRITIQUE : Importation et instanciation explicite du connecteur
64
  from nemo.core.connectors.save_restore_connector import SaveRestoreConnector
65
+
66
+ # On force NeMo à utiliser cette instance précise pour éviter le bug __init__()
67
  model = nemo_asr.models.ASRModel.restore_from(
68
  nemo_file,
69
  map_location=torch.device(DEVICE),
70
+ save_restore_connector=SaveRestoreConnector()
71
  )
72
 
73
+ model.eval()
74
  if DEVICE == "cuda":
75
  model = model.half()
76
 
 
91
  yield "❌ Aucune vidéo sélectionnée.", None
92
  return
93
 
94
+ yield "⏳ Extraction audio...", None
95
  full_wav = os.path.join(tmp_dir, "full.wav")
96
  subprocess.run(f"ffmpeg -y -i {shlex.quote(video_in)} -vn -ac 1 -ar 16000 {full_wav}", shell=True, check=True)
97
 
98
+ yield f"⏳ Segmentation ({SEGMENT_DURATION}s)...", None
99
  subprocess.run(f"ffmpeg -i {full_wav} -f segment -segment_time {SEGMENT_DURATION} -c copy {os.path.join(tmp_dir, 'seg_%03d.wav')}", shell=True, check=True)
100
 
101
  files = sorted(glob.glob(os.path.join(tmp_dir, "seg_*.wav")))
 
102
  valid_segments = [f for f in files if os.path.getsize(f) > 1500]
103
 
104
  if not valid_segments:
105
+ yield "❌ Erreur : Audio trop court ou invalide.", None
106
  return
107
 
108
+ yield f"⏳ Chargement de {model_name}...", None
109
  model = get_model(model_name)
110
 
111
  yield f"🎙️ Transcription de {len(valid_segments)} segments...", None
 
129
  "end": base_time + ((i+1) * gap)
130
  })
131
 
132
+ yield "⏳ Encodage vidéo...", None
133
  srt_path = os.path.join(tmp_dir, "final.srt")
134
  with open(srt_path, "w", encoding="utf-8") as f:
135
  for i in range(0, len(all_words_ts), 6):
 
140
  out_path = os.path.abspath(f"resultat_{int(time.time())}.mp4")
141
  safe_srt = srt_path.replace("\\", "/").replace(":", "\\:")
142
 
143
+ cmd = f"ffmpeg -y -i {shlex.quote(video_in)} -vf \"subtitles='{safe_srt}':force_style='Alignment=2,FontSize=18,PrimaryColour=&H00FFFF,Outline=1'\" -c:v libx264 -preset ultrafast -c:a copy {out_path}"
144
  subprocess.run(cmd, shell=True, check=True)
145
 
146
  yield "✅ Terminé !", out_path
 
161
  m_input = gr.Dropdown(choices=list(MODELS.keys()), value="Soloni V3 (TDT-CTC)", label="Modèle")
162
  run_btn = gr.Button("🚀 GÉNÉRER", variant="primary")
163
 
 
164
  if EXAMPLE_PATH:
165
  gr.Examples(examples=[[EXAMPLE_PATH, "Soloni V3 (TDT-CTC)"]], inputs=[v_input, m_input])
166
 
167
  with gr.Column():
168
  status = gr.Markdown("### État\nPrêt.")
169
+ v_output = gr.Video(label="Résultat")
170
 
171
  run_btn.click(pipeline, [v_input, m_input], [status, v_output])
172