binaryMao commited on
Commit
bd18b66
·
verified ·
1 Parent(s): b7b07c0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +23 -38
app.py CHANGED
@@ -1,9 +1,6 @@
1
  # -*- coding: utf-8 -*-
2
  """
3
- ROBOTSMALI — Sous-titrage Bambara (VERSION COMPLÈTE V6.3)
4
- - Tous les modèles inclus
5
- - Correction du clic sur exemple
6
- - Correction de l'affichage vidéo (Output Fix)
7
  """
8
  import os
9
  import shlex
@@ -25,9 +22,6 @@ import gradio as gr
25
 
26
  # ---------------------------- # CONFIGURATION # ----------------------------
27
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
28
- random.seed(1234)
29
- np.random.seed(1234)
30
- torch.manual_seed(1234)
31
 
32
  MODELS = {
33
  "Soloni V1 (RNNT)": ("RobotsMali/soloni-114m-tdt-ctc-v1", "rnnt"),
@@ -54,9 +48,7 @@ def run_cmd(cmd):
54
 
55
  def load_model(name):
56
  if name in _cache: return _cache[name]
57
- if len(_cache) > 0:
58
- _cache.clear()
59
- if torch.cuda.is_available(): torch.cuda.empty_cache()
60
 
61
  repo, mode = MODELS[name]
62
  folder = snapshot_download(repo, local_dir_use_symlinks=False)
@@ -74,9 +66,12 @@ def load_model(name):
74
  return model
75
 
76
  def burn_subtitles(video_path, words, duration):
77
- # On crée le fichier dans le dossier courant pour éviter les erreurs de permissions
78
- out_name = f"resultat_robotsmali_{int(time.time())}.mp4"
79
- out_path = os.path.abspath(out_name)
 
 
 
80
 
81
  chunk_size = 7
82
  with tempfile.NamedTemporaryFile(suffix=".srt", mode="w", encoding="utf-8", delete=False) as tf:
@@ -91,12 +86,12 @@ def burn_subtitles(video_path, words, duration):
91
  tf.write(f"{i+1}\n{t_srt(start)} --> {t_srt(end)}\n{txt}\n\n")
92
  srt_name = tf.name
93
 
94
- # Commande ultra-compatible pour navigateur (H.264, YUV420p)
95
- vf = f"subtitles={shlex.quote(srt_name)}:force_style='Fontsize=22,PrimaryColour=&HFFFFFF&,OutlineColour=&H000000&'"
96
  cmd = (
97
  f'ffmpeg -hide_banner -loglevel error -y -i {shlex.quote(video_path)} '
98
  f'-vf {shlex.quote(vf)} -c:v libx264 -pix_fmt yuv420p -preset ultrafast -crf 28 '
99
- f'-c:a aac -b:a 128k {shlex.quote(out_path)}'
100
  )
101
  run_cmd(cmd)
102
  os.remove(srt_name)
@@ -109,16 +104,14 @@ def pipeline(video_input, model_name):
109
  if not video_input: return "❌ Vidéo introuvable", None, None
110
 
111
  yield "⏳ Phase 1/3 : Analyse Audio...", None, None
112
- with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tf:
113
- wav_path = tf.name
114
-
115
  run_cmd(f'ffmpeg -hide_banner -loglevel error -y -i {shlex.quote(video_input)} -vn -ac 1 -ar 16000 -f wav {shlex.quote(wav_path)}')
116
 
117
  dur_out = subprocess.run(f'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 {shlex.quote(video_input)}',
118
  shell=True, stdout=subprocess.PIPE, text=True).stdout
119
  duration = float(dur_out.strip()) if dur_out.strip() else 10.0
120
 
121
- yield f"⏳ Phase 2/3 : Transcription {model_name}...", None, None
122
  model = load_model(model_name)
123
  res = model.transcribe([wav_path])[0]
124
  text = res.text if hasattr(res, 'text') else str(res)
@@ -126,44 +119,36 @@ def pipeline(video_input, model_name):
126
 
127
  if not words: return "⚠️ Pas de parole détectée", None, None
128
 
129
- yield "⏳ Phase 3/3 : Encodage de la vidéo...", None, None
130
  final_v = burn_subtitles(video_input, words, duration)
131
 
132
- if os.path.exists(wav_path): os.remove(wav_path)
133
-
134
- yield "✅ Succès !", final_v, final_v
135
 
136
  except Exception as e:
137
- traceback.print_exc()
138
  yield f"❌ Erreur: {str(e)}", None, None
139
 
140
  # ---------------------------- # INTERFACE # ----------------------------
141
 
142
  custom_css = """
143
  body { background-color: #0b0e14; }
144
- .gradio-container { background: rgba(17, 25, 40, 0.8) !important; border-radius: 20px; border: 1px solid rgba(255, 255, 255, 0.1); }
145
- #header { text-align: center; padding: 20px; }
146
- .gr-button-primary { background: linear-gradient(135deg, #059669, #10b981) !important; }
147
  """
148
 
149
  with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
150
- with gr.Column(elem_id="header"):
151
- gr.HTML("<h1 style='color:#facc15;'>🤖 ROBOTSMALI</h1><p style='color:#94a3b8;'>Sous-titrage Bambara Intégral</p>")
152
 
153
  with gr.Row():
154
  with gr.Column():
155
- v_in = gr.Video(label="Source", mirror_webcam=False)
156
  m_sel = gr.Dropdown(list(MODELS.keys()), value="Soloba V1 (CTC)", label="Modèle IA")
157
  btn = gr.Button("🚀 GÉNÉRER", variant="primary")
158
  with gr.Column():
159
- status = gr.Markdown("*Prêt*")
160
- v_out = gr.Video(label="Vidéo Sous-titrée")
161
- f_out = gr.File(label="Lien de secours (Téléchargement)")
162
 
163
  gr.Examples(examples=VIDEO_EXAMPLES, inputs=[v_in, m_sel], cache_examples=False)
164
-
165
- # On renvoie vers status, le lecteur vidéo ET le composant fichier
166
  btn.click(pipeline, [v_in, m_sel], [status, v_out, f_out])
167
 
168
- if __name__ == "__main__":
169
- demo.launch(share=True, debug=True)
 
1
  # -*- coding: utf-8 -*-
2
  """
3
+ ROBOTSMALI — Sous-titrage Bambara (VERSION COMPLÈTE V6.4 - FORCE OUTPUT)
 
 
 
4
  """
5
  import os
6
  import shlex
 
22
 
23
  # ---------------------------- # CONFIGURATION # ----------------------------
24
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
 
 
 
25
 
26
  MODELS = {
27
  "Soloni V1 (RNNT)": ("RobotsMali/soloni-114m-tdt-ctc-v1", "rnnt"),
 
48
 
49
  def load_model(name):
50
  if name in _cache: return _cache[name]
51
+ _cache.clear() # Nettoyage RAM
 
 
52
 
53
  repo, mode = MODELS[name]
54
  folder = snapshot_download(repo, local_dir_use_symlinks=False)
 
66
  return model
67
 
68
  def burn_subtitles(video_path, words, duration):
69
+ # SAUVEGARDE LOCALE PERSISTANTE
70
+ out_name = f"resultat_final.mp4"
71
+ out_path = os.path.join(os.getcwd(), out_name)
72
+
73
+ # Nettoyage d'un ancien fichier si présent
74
+ if os.path.exists(out_path): os.remove(out_path)
75
 
76
  chunk_size = 7
77
  with tempfile.NamedTemporaryFile(suffix=".srt", mode="w", encoding="utf-8", delete=False) as tf:
 
86
  tf.write(f"{i+1}\n{t_srt(start)} --> {t_srt(end)}\n{txt}\n\n")
87
  srt_name = tf.name
88
 
89
+ # Commande FFmpeg forcée sur les codecs web standards
90
+ vf = f"subtitles={shlex.quote(srt_name)}:force_style='Fontsize=22,PrimaryColour=&HFFFFFF&'"
91
  cmd = (
92
  f'ffmpeg -hide_banner -loglevel error -y -i {shlex.quote(video_path)} '
93
  f'-vf {shlex.quote(vf)} -c:v libx264 -pix_fmt yuv420p -preset ultrafast -crf 28 '
94
+ f'-c:a aac -b:a 128k -movflags +faststart {shlex.quote(out_path)}'
95
  )
96
  run_cmd(cmd)
97
  os.remove(srt_name)
 
104
  if not video_input: return "❌ Vidéo introuvable", None, None
105
 
106
  yield "⏳ Phase 1/3 : Analyse Audio...", None, None
107
+ wav_path = os.path.abspath("temp_audio.wav")
 
 
108
  run_cmd(f'ffmpeg -hide_banner -loglevel error -y -i {shlex.quote(video_input)} -vn -ac 1 -ar 16000 -f wav {shlex.quote(wav_path)}')
109
 
110
  dur_out = subprocess.run(f'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 {shlex.quote(video_input)}',
111
  shell=True, stdout=subprocess.PIPE, text=True).stdout
112
  duration = float(dur_out.strip()) if dur_out.strip() else 10.0
113
 
114
+ yield f"⏳ Phase 2/3 : Transcription IA...", None, None
115
  model = load_model(model_name)
116
  res = model.transcribe([wav_path])[0]
117
  text = res.text if hasattr(res, 'text') else str(res)
 
119
 
120
  if not words: return "⚠️ Pas de parole détectée", None, None
121
 
122
+ yield "⏳ Phase 3/3 : Création de la vidéo...", None, None
123
  final_v = burn_subtitles(video_input, words, duration)
124
 
125
+ yield "✅ Succès ! Vidéo générée.", final_v, final_v
 
 
126
 
127
  except Exception as e:
 
128
  yield f"❌ Erreur: {str(e)}", None, None
129
 
130
  # ---------------------------- # INTERFACE # ----------------------------
131
 
132
  custom_css = """
133
  body { background-color: #0b0e14; }
134
+ .gradio-container { background: rgba(17, 25, 40, 0.9) !important; border-radius: 15px; }
135
+ .gr-button-primary { background: #10b981 !important; }
 
136
  """
137
 
138
  with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
139
+ gr.HTML("<h1 style='color:#facc15; text-align:center;'>🤖 ROBOTSMALI V6.4</h1>")
 
140
 
141
  with gr.Row():
142
  with gr.Column():
143
+ v_in = gr.Video(label="Source")
144
  m_sel = gr.Dropdown(list(MODELS.keys()), value="Soloba V1 (CTC)", label="Modèle IA")
145
  btn = gr.Button("🚀 GÉNÉRER", variant="primary")
146
  with gr.Column():
147
+ status = gr.Markdown("### Statut\n*Prêt*")
148
+ v_out = gr.Video(label="Résultat")
149
+ f_out = gr.File(label="Télécharger le fichier MP4")
150
 
151
  gr.Examples(examples=VIDEO_EXAMPLES, inputs=[v_in, m_sel], cache_examples=False)
 
 
152
  btn.click(pipeline, [v_in, m_sel], [status, v_out, f_out])
153
 
154
+ demo.launch(share=True, debug=True)